Merge "Add layout for 1x1 size, update 2x1" into sc-dev
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
index 5ded446..f6bfaa3 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -18,9 +18,10 @@
}
public static final class AppSearchManager.SearchContext.Builder {
- ctor public AppSearchManager.SearchContext.Builder();
+ ctor @Deprecated public AppSearchManager.SearchContext.Builder();
+ ctor public AppSearchManager.SearchContext.Builder(@NonNull String);
method @NonNull public android.app.appsearch.AppSearchManager.SearchContext build();
- method @NonNull public android.app.appsearch.AppSearchManager.SearchContext.Builder setDatabaseName(@NonNull String);
+ method @Deprecated @NonNull public android.app.appsearch.AppSearchManager.SearchContext.Builder setDatabaseName(@NonNull String);
}
public interface AppSearchMigrationHelper {
@@ -115,7 +116,6 @@
public abstract static class AppSearchSchema.PropertyConfig {
method public int getCardinality();
- method public int getDataType();
method @NonNull public String getName();
field public static final int CARDINALITY_OPTIONAL = 2; // 0x2
field public static final int CARDINALITY_REPEATED = 1; // 0x1
@@ -180,14 +180,15 @@
method public int getScore();
method public long getTtlMillis();
method @NonNull public String getUri();
- field public static final String DEFAULT_NAMESPACE = "";
+ field @Deprecated public static final String DEFAULT_NAMESPACE = "";
}
public static class GenericDocument.Builder<BuilderType extends android.app.appsearch.GenericDocument.Builder> {
- ctor public GenericDocument.Builder(@NonNull String, @NonNull String);
+ ctor @Deprecated public GenericDocument.Builder(@NonNull String, @NonNull String);
+ ctor public GenericDocument.Builder(@NonNull String, @NonNull String, @NonNull String);
method @NonNull public android.app.appsearch.GenericDocument build();
method @NonNull public BuilderType setCreationTimestampMillis(long);
- method @NonNull public BuilderType setNamespace(@NonNull String);
+ method @Deprecated @NonNull public BuilderType setNamespace(@NonNull String);
method @NonNull public BuilderType setPropertyBoolean(@NonNull String, @NonNull boolean...);
method @NonNull public BuilderType setPropertyBytes(@NonNull String, @NonNull byte[]...);
method @NonNull public BuilderType setPropertyDocument(@NonNull String, @NonNull android.app.appsearch.GenericDocument...);
@@ -206,12 +207,13 @@
}
public static final class GetByUriRequest.Builder {
- ctor public GetByUriRequest.Builder();
+ ctor @Deprecated public GetByUriRequest.Builder();
+ ctor public GetByUriRequest.Builder(@NonNull String);
method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.lang.String...);
method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.GetByUriRequest build();
- method @NonNull public android.app.appsearch.GetByUriRequest.Builder setNamespace(@NonNull String);
+ method @Deprecated @NonNull public android.app.appsearch.GetByUriRequest.Builder setNamespace(@NonNull String);
}
public class GlobalSearchSession implements java.io.Closeable {
@@ -242,11 +244,12 @@
}
public static final class RemoveByUriRequest.Builder {
- ctor public RemoveByUriRequest.Builder();
+ ctor @Deprecated public RemoveByUriRequest.Builder();
+ ctor public RemoveByUriRequest.Builder(@NonNull String);
method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUris(@NonNull java.lang.String...);
method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUris(@NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.RemoveByUriRequest build();
- method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder setNamespace(@NonNull String);
+ method @Deprecated @NonNull public android.app.appsearch.RemoveByUriRequest.Builder setNamespace(@NonNull String);
}
public final class ReportUsageRequest {
@@ -256,30 +259,50 @@
}
public static final class ReportUsageRequest.Builder {
- ctor public ReportUsageRequest.Builder();
+ ctor @Deprecated public ReportUsageRequest.Builder();
+ ctor public ReportUsageRequest.Builder(@NonNull String);
method @NonNull public android.app.appsearch.ReportUsageRequest build();
- method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setNamespace(@NonNull String);
+ method @Deprecated @NonNull public android.app.appsearch.ReportUsageRequest.Builder setNamespace(@NonNull String);
method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setUri(@NonNull String);
method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setUsageTimeMillis(long);
}
public final class SearchResult {
method @NonNull public String getDatabaseName();
- method @NonNull public android.app.appsearch.GenericDocument getDocument();
+ method @Deprecated @NonNull public android.app.appsearch.GenericDocument getDocument();
+ method @NonNull public android.app.appsearch.GenericDocument getGenericDocument();
method @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatches();
method @NonNull public String getPackageName();
}
+ public static final class SearchResult.Builder {
+ ctor public SearchResult.Builder(@NonNull String, @NonNull String);
+ method @NonNull public android.app.appsearch.SearchResult.Builder addMatch(@NonNull android.app.appsearch.SearchResult.MatchInfo);
+ method @NonNull public android.app.appsearch.SearchResult build();
+ method @NonNull public android.app.appsearch.SearchResult.Builder setGenericDocument(@NonNull android.app.appsearch.GenericDocument);
+ }
+
public static final class SearchResult.MatchInfo {
method @NonNull public CharSequence getExactMatch();
- method @NonNull public android.app.appsearch.SearchResult.MatchRange getExactMatchPosition();
+ method @Deprecated @NonNull public android.app.appsearch.SearchResult.MatchRange getExactMatchPosition();
+ method @NonNull public android.app.appsearch.SearchResult.MatchRange getExactMatchRange();
method @NonNull public String getFullText();
method @NonNull public String getPropertyPath();
method @NonNull public CharSequence getSnippet();
- method @NonNull public android.app.appsearch.SearchResult.MatchRange getSnippetPosition();
+ method @Deprecated @NonNull public android.app.appsearch.SearchResult.MatchRange getSnippetPosition();
+ method @NonNull public android.app.appsearch.SearchResult.MatchRange getSnippetRange();
+ }
+
+ public static final class SearchResult.MatchInfo.Builder {
+ ctor public SearchResult.MatchInfo.Builder();
+ method @NonNull public android.app.appsearch.SearchResult.MatchInfo build();
+ method @NonNull public android.app.appsearch.SearchResult.MatchInfo.Builder setExactMatchRange(@NonNull android.app.appsearch.SearchResult.MatchRange);
+ method @NonNull public android.app.appsearch.SearchResult.MatchInfo.Builder setPropertyPath(@NonNull String);
+ method @NonNull public android.app.appsearch.SearchResult.MatchInfo.Builder setSnippetRange(@NonNull android.app.appsearch.SearchResult.MatchRange);
}
public static final class SearchResult.MatchRange {
+ ctor public SearchResult.MatchRange(int, int);
method public int getEnd();
method public int getStart();
}
@@ -361,6 +384,19 @@
method @NonNull public java.util.List<android.app.appsearch.SetSchemaResponse.MigrationFailure> getMigrationFailures();
}
+ public static final class SetSchemaResponse.Builder {
+ ctor public SetSchemaResponse.Builder();
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addDeletedType(@NonNull String);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addDeletedTypes(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addIncompatibleType(@NonNull String);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addIncompatibleTypes(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigratedType(@NonNull String);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigratedTypes(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigrationFailure(@NonNull android.app.appsearch.SetSchemaResponse.MigrationFailure);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigrationFailures(@NonNull java.util.Collection<android.app.appsearch.SetSchemaResponse.MigrationFailure>);
+ method @NonNull public android.app.appsearch.SetSchemaResponse build();
+ }
+
public static class SetSchemaResponse.MigrationFailure {
method @NonNull public android.app.appsearch.AppSearchResult<java.lang.Void> getAppSearchResult();
method @NonNull public String getNamespace();
@@ -368,11 +404,23 @@
method @NonNull public String getUri();
}
+ public static final class SetSchemaResponse.MigrationFailure.Builder {
+ ctor public SetSchemaResponse.MigrationFailure.Builder();
+ method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure build();
+ method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure.Builder setAppSearchResult(@NonNull android.app.appsearch.AppSearchResult<java.lang.Void>);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure.Builder setNamespace(@NonNull String);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure.Builder setSchemaType(@NonNull String);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure.Builder setUri(@NonNull String);
+ }
+
}
package android.app.appsearch.exceptions {
public class AppSearchException extends java.lang.Exception {
+ ctor public AppSearchException(int);
+ ctor public AppSearchException(int, @Nullable String);
+ ctor public AppSearchException(int, @Nullable String, @Nullable Throwable);
method public int getResultCode();
method @NonNull public <T> android.app.appsearch.AppSearchResult<T> toAppSearchResult();
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index a62bb50..0c6b86b 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -30,17 +30,19 @@
* Provides access to the centralized AppSearch index maintained by the system.
*
* <p>AppSearch is a search library for managing structured data featuring:
+ *
* <ul>
- * <li>A fully offline on-device solution
- * <li>A set of APIs for applications to index documents and retrieve them via full-text search
- * <li>APIs for applications to allow the System to display their content on system UI surfaces
- * <li>Similarly, APIs for applications to allow the System to share their content with other
- * specified applications.
+ * <li>A fully offline on-device solution
+ * <li>A set of APIs for applications to index documents and retrieve them via full-text search
+ * <li>APIs for applications to allow the System to display their content on system UI surfaces
+ * <li>Similarly, APIs for applications to allow the System to share their content with other
+ * specified applications.
* </ul>
*
* <p>Applications create a database by opening an {@link AppSearchSession}.
*
* <p>Example:
+ *
* <pre>
* AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class);
*
@@ -51,11 +53,12 @@
* });</pre>
*
* <p>After opening the session, a schema must be set in order to define the organizational
- * structure of data. The schema is set by calling {@link AppSearchSession#setSchema}. The schema
- * is composed of a collection of {@link AppSearchSchema} objects, each of which defines a unique
- * type of data.
+ * structure of data. The schema is set by calling {@link AppSearchSession#setSchema}. The schema is
+ * composed of a collection of {@link AppSearchSchema} objects, each of which defines a unique type
+ * of data.
*
* <p>Example:
+ *
* <pre>
* AppSearchSchema emailSchemaType = new AppSearchSchema.Builder("Email")
* .addProperty(new StringPropertyConfig.Builder("subject")
@@ -73,15 +76,16 @@
* });</pre>
*
* <p>The basic unit of data in AppSearch is represented as a {@link GenericDocument} object,
- * containing a URI, namespace, time-to-live, score, and properties. A namespace organizes a
- * logical group of documents. For example, a namespace can be created to group documents on a
- * per-account basis. A URI identifies a single document within a namespace. The combination
- * of URI and namespace uniquely identifies a {@link GenericDocument} in the database.
+ * containing a URI, namespace, time-to-live, score, and properties. A namespace organizes a logical
+ * group of documents. For example, a namespace can be created to group documents on a per-account
+ * basis. A URI identifies a single document within a namespace. The combination of URI and
+ * namespace uniquely identifies a {@link GenericDocument} in the database.
*
- * <p>Once the schema has been set, {@link GenericDocument} objects can be put into the database
- * and indexed by calling {@link AppSearchSession#put}.
+ * <p>Once the schema has been set, {@link GenericDocument} objects can be put into the database and
+ * indexed by calling {@link AppSearchSession#put}.
*
* <p>Example:
+ *
* <pre>
* // Although for this example we use GenericDocument directly, we recommend extending
* // GenericDocument to create specific types (i.e. Email) with specific setters/getters.
@@ -106,18 +110,12 @@
* and namespace.
*
* <p>Document removal is done either by time-to-live expiration, or explicitly calling a remove
- * operation. Remove operations can be done by URI and namespace via
- * {@link AppSearchSession#remove(RemoveByUriRequest, Executor, BatchResultCallback)},
- * or by query via {@link AppSearchSession#remove(String, SearchSpec, Executor, Consumer)}.
+ * operation. Remove operations can be done by URI and namespace via {@link
+ * AppSearchSession#remove(RemoveByUriRequest, Executor, BatchResultCallback)}, or by query via
+ * {@link AppSearchSession#remove(String, SearchSpec, Executor, Consumer)}.
*/
@SystemService(Context.APP_SEARCH_SERVICE)
public class AppSearchManager {
- /**
- * The default empty database name.
- *
- * @hide
- */
- public static final String DEFAULT_DATABASE_NAME = "";
private final IAppSearchManager mService;
private final Context mContext;
@@ -149,10 +147,42 @@
/** Builder for {@link SearchContext} objects. */
public static final class Builder {
- private String mDatabaseName = DEFAULT_DATABASE_NAME;
+ private String mDatabaseName;
private boolean mBuilt = false;
/**
+ * TODO(b/181887768): This method exists only for dogfooder transition and must be
+ * removed.
+ *
+ * @deprecated Please supply the databaseName in {@link #Builder(String)} instead. This
+ * method exists only for dogfooder transition and must be removed.
+ */
+ @Deprecated
+ public Builder() {
+ mDatabaseName = "";
+ }
+
+ /**
+ * Creates a new {@link SearchContext.Builder}.
+ *
+ * <p>{@link AppSearchSession} will create or open a database under the given name.
+ *
+ * <p>Databases with different names are fully separate with distinct types, namespaces,
+ * and data.
+ *
+ * <p>Database name cannot contain {@code '/'}.
+ *
+ * @param databaseName The name of the database.
+ * @throws IllegalArgumentException if the databaseName contains {@code '/'}.
+ */
+ public Builder(@NonNull String databaseName) {
+ Objects.requireNonNull(databaseName);
+ Preconditions.checkArgument(
+ !databaseName.contains("/"), "Database name cannot contain '/'");
+ mDatabaseName = databaseName;
+ }
+
+ /**
* Sets the name of the database associated with {@link AppSearchSession}.
*
* <p>{@link AppSearchSession} will create or open a database under the given name.
@@ -164,16 +194,21 @@
*
* <p>If not specified, defaults to the empty string.
*
+ * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
+ * removed.
+ *
* @param databaseName The name of the database.
* @throws IllegalArgumentException if the databaseName contains {@code '/'}.
+ * @deprecated Please supply the databaseName in {@link #Builder(String)} instead. This
+ * method exists only for dogfooder transition and must be removed.
*/
+ @Deprecated
@NonNull
public Builder setDatabaseName(@NonNull String databaseName) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Objects.requireNonNull(databaseName);
- if (databaseName.contains("/")) {
- throw new IllegalArgumentException("Database name cannot contain '/'");
- }
+ Preconditions.checkArgument(
+ !databaseName.contains("/"), "Database name cannot contain '/'");
mDatabaseName = databaseName;
return this;
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index f379739..486acb4 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -241,6 +241,7 @@
documentBundles.add(documents.get(i).getBundle());
}
try {
+ // TODO(b/173532925) a timestamp needs to be sent here to calculate binder latency
mService.putDocuments(mPackageName, mDatabaseName, documentBundles, mUserId,
new IAppSearchBatchResultCallback.Stub() {
public void onResult(AppSearchBatchResult result) {
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java
index d394904..77740f8 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java
@@ -152,14 +152,14 @@
/** The builder class for {@link AppSearchEmail}. */
public static class Builder extends GenericDocument.Builder<AppSearchEmail.Builder> {
-
/**
* Creates a new {@link AppSearchEmail.Builder}
*
+ * @param namespace The namespace of the Email.
* @param uri The Uri of the Email.
*/
- public Builder(@NonNull String uri) {
- super(uri, SCHEMA_TYPE);
+ public Builder(@NonNull String namespace, @NonNull String uri) {
+ super(namespace, uri, SCHEMA_TYPE);
}
/** Sets the from address of {@link AppSearchEmail} */
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
index 8bf438d..2cf5271 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
@@ -309,7 +309,11 @@
return mBundle.getString(NAME_FIELD, "");
}
- /** Returns the type of data the property contains (e.g. string, int, bytes, etc). */
+ /**
+ * Returns the type of data the property contains (e.g. string, int, bytes, etc).
+ *
+ * @hide
+ */
public @DataType int getDataType() {
return mBundle.getInt(DATA_TYPE_FIELD, -1);
}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
index 72bb9f3..4ce95ea 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
@@ -46,8 +46,14 @@
public class GenericDocument {
private static final String TAG = "AppSearchGenericDocumen";
- /** The default empty namespace. */
- public static final String DEFAULT_NAMESPACE = "";
+ /**
+ * The default empty namespace.
+ *
+ * <p>TODO(b/181887768): This exists only for dogfooder transition and must be removed.
+ *
+ * @deprecated This exists only for dogfooder transition and must be removed.
+ */
+ @Deprecated public static final String DEFAULT_NAMESPACE = "";
/** The maximum number of elements in a repeatable field. */
private static final int MAX_REPEATED_PROPERTY_LENGTH = 100;
@@ -141,7 +147,7 @@
/** Returns the namespace of the {@link GenericDocument}. */
@NonNull
public String getNamespace() {
- return mBundle.getString(NAMESPACE_FIELD, DEFAULT_NAMESPACE);
+ return mBundle.getString(NAMESPACE_FIELD, /*defaultValue=*/ "");
}
/** Returns the {@link AppSearchSchema} type of the {@link GenericDocument}. */
@@ -579,6 +585,9 @@
*
* <p>Once {@link #build} is called, the instance can no longer be used.
*
+ * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
+ * removed.
+ *
* @param uri the URI to set for the {@link GenericDocument}.
* @param schemaType the {@link AppSearchSchema} type of the {@link GenericDocument}. The
* provided {@code schemaType} must be defined using {@link AppSearchSession#setSchema}
@@ -586,7 +595,10 @@
* using {@link AppSearchSession#put}. Otherwise, the document will be rejected by
* {@link AppSearchSession#put} with result code {@link
* AppSearchResult#RESULT_NOT_FOUND}.
+ * @deprecated Please supply the namespace in {@link #Builder(String, String, String)}
+ * instead. This method exists only for dogfooder transition and must be removed.
*/
+ @Deprecated
@SuppressWarnings("unchecked")
public Builder(@NonNull String uri, @NonNull String schemaType) {
Preconditions.checkNotNull(uri);
@@ -604,6 +616,41 @@
}
/**
+ * Creates a new {@link GenericDocument.Builder}.
+ *
+ * <p>Once {@link #build} is called, the instance can no longer be used.
+ *
+ * <p>URIs are unique within a namespace.
+ *
+ * <p>The number of namespaces per app should be kept small for efficiency reasons.
+ *
+ * @param namespace the namespace to set for the {@link GenericDocument}.
+ * @param uri the URI to set for the {@link GenericDocument}.
+ * @param schemaType the {@link AppSearchSchema} type of the {@link GenericDocument}. The
+ * provided {@code schemaType} must be defined using {@link AppSearchSession#setSchema}
+ * prior to inserting a document of this {@code schemaType} into the AppSearch index
+ * using {@link AppSearchSession#put}. Otherwise, the document will be rejected by
+ * {@link AppSearchSession#put} with result code {@link
+ * AppSearchResult#RESULT_NOT_FOUND}.
+ */
+ @SuppressWarnings("unchecked")
+ public Builder(@NonNull String namespace, @NonNull String uri, @NonNull String schemaType) {
+ Preconditions.checkNotNull(namespace);
+ Preconditions.checkNotNull(uri);
+ Preconditions.checkNotNull(schemaType);
+ mBuilderTypeInstance = (BuilderType) this;
+ mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace);
+ mBundle.putString(GenericDocument.URI_FIELD, uri);
+ mBundle.putString(GenericDocument.SCHEMA_TYPE_FIELD, schemaType);
+ // Set current timestamp for creation timestamp by default.
+ mBundle.putLong(
+ GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, System.currentTimeMillis());
+ mBundle.putLong(GenericDocument.TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS);
+ mBundle.putInt(GenericDocument.SCORE_FIELD, DEFAULT_SCORE);
+ mBundle.putBundle(PROPERTIES_FIELD, mProperties);
+ }
+
+ /**
* Sets the app-defined namespace this document resides in. No special values are reserved
* or understood by the infrastructure.
*
@@ -611,8 +658,14 @@
*
* <p>The number of namespaces per app should be kept small for efficiency reasons.
*
+ * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
+ * removed.
+ *
* @throws IllegalStateException if the builder has already been used.
+ * @deprecated Please supply the namespace in {@link #Builder(String, String, String)}
+ * instead. This method exists only for dogfooder transition and must be removed.
*/
+ @Deprecated
@NonNull
public BuilderType setNamespace(@NonNull String namespace) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
index 17266f8..6881a27 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
@@ -107,19 +107,40 @@
* <p>Once {@link #build} is called, the instance can no longer be used.
*/
public static final class Builder {
- private String mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+ private String mNamespace;
private final Set<String> mUris = new ArraySet<>();
private final Map<String, List<String>> mProjectionTypePropertyPaths = new ArrayMap<>();
private boolean mBuilt = false;
/**
+ * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
+ *
+ * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
+ * exists only for dogfooder transition and must be removed.
+ */
+ @Deprecated
+ public Builder() {
+ mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+ }
+
+ /** Creates a {@link GetByUriRequest.Builder} instance. */
+ public Builder(@NonNull String namespace) {
+ mNamespace = Preconditions.checkNotNull(namespace);
+ }
+
+ /**
* Sets the namespace to retrieve documents for.
*
- * <p>If this is not called, the namespace defaults to {@link
- * GenericDocument#DEFAULT_NAMESPACE}.
+ * <p>If this is not called, the namespace defaults to an empty string.
+ *
+ * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
+ * removed.
*
* @throws IllegalStateException if the builder has already been used.
+ * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
+ * exists only for dogfooder transition and must
*/
+ @Deprecated
@NonNull
public Builder setNamespace(@NonNull String namespace) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
index 39b53b6..455cf3a 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
@@ -59,17 +59,39 @@
* <p>Once {@link #build} is called, the instance can no longer be used.
*/
public static final class Builder {
- private String mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+ private String mNamespace;
private final Set<String> mUris = new ArraySet<>();
private boolean mBuilt = false;
/**
+ * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
+ *
+ * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
+ * exists only for dogfooder transition and must be removed.
+ */
+ @Deprecated
+ public Builder() {
+ mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+ }
+
+ /** Creates a {@link RemoveByUriRequest.Builder} instance. */
+ public Builder(@NonNull String namespace) {
+ mNamespace = Preconditions.checkNotNull(namespace);
+ }
+
+ /**
* Sets the namespace to remove documents for.
*
- * <p>If this is not set, it defaults to {@link GenericDocument#DEFAULT_NAMESPACE}.
+ * <p>If this is not set, it defaults to an empty string.
+ *
+ * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
+ * removed.
*
* @throws IllegalStateException if the builder has already been used.
+ * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
+ * exists only for dogfooder transition and must
*/
+ @Deprecated
@NonNull
public Builder setNamespace(@NonNull String namespace) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
index 2bfcf28..2cd08c6 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
@@ -62,18 +62,40 @@
/** Builder for {@link ReportUsageRequest} objects. */
public static final class Builder {
- private String mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+ private String mNamespace;
private String mUri;
private Long mUsageTimeMillis;
private boolean mBuilt = false;
/**
+ * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
+ *
+ * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
+ * exists only for dogfooder transition and must be removed.
+ */
+ @Deprecated
+ public Builder() {
+ mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+ }
+
+ /** Creates a {@link ReportUsageRequest.Builder} instance. */
+ public Builder(@NonNull String namespace) {
+ mNamespace = Preconditions.checkNotNull(namespace);
+ }
+
+ /**
* Sets which namespace the document being used belongs to.
*
- * <p>If this is not set, it defaults to {@link GenericDocument#DEFAULT_NAMESPACE}.
+ * <p>If this is not set, it defaults to an empty string.
+ *
+ * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
+ * removed.
*
* @throws IllegalStateException if the builder has already been used
+ * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
+ * exists only for dogfooder transition and must
*/
+ @Deprecated
@NonNull
public ReportUsageRequest.Builder setNamespace(@NonNull String namespace) {
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 f34034b..cb20849 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
@@ -32,7 +32,7 @@
* <p>This allows clients to obtain:
*
* <ul>
- * <li>The document which matched, using {@link #getDocument}
+ * <li>The document which matched, using {@link #getGenericDocument}
* <li>Information about which properties in the document matched, and "snippet" information
* containing textual summaries of the document's matches, using {@link #getMatches}
* </ul>
@@ -43,17 +43,10 @@
* @see SearchResults
*/
public final class SearchResult {
- /** @hide */
- public static final String DOCUMENT_FIELD = "document";
-
- /** @hide */
- public static final String MATCHES_FIELD = "matches";
-
- /** @hide */
- public static final String PACKAGE_NAME_FIELD = "packageName";
-
- /** @hide */
- public static final String DATABASE_NAME_FIELD = "databaseName";
+ static final String DOCUMENT_FIELD = "document";
+ static final String MATCHES_FIELD = "matches";
+ static final String PACKAGE_NAME_FIELD = "packageName";
+ static final String DATABASE_NAME_FIELD = "databaseName";
@NonNull private final Bundle mBundle;
@@ -74,13 +67,20 @@
return mBundle;
}
+ /** @deprecated TODO(b/181887768): This method exists only for dogfooder transition. */
+ @NonNull
+ @Deprecated
+ public GenericDocument getDocument() {
+ return getGenericDocument();
+ }
+
/**
* Contains the matching {@link GenericDocument}.
*
* @return Document object which matched the query.
*/
@NonNull
- public GenericDocument getDocument() {
+ public GenericDocument getGenericDocument() {
if (mDocument == null) {
mDocument =
new GenericDocument(
@@ -104,7 +104,7 @@
Preconditions.checkNotNull(mBundle.getParcelableArrayList(MATCHES_FIELD));
mMatches = new ArrayList<>(matchBundles.size());
for (int i = 0; i < matchBundles.size(); i++) {
- MatchInfo matchInfo = new MatchInfo(getDocument(), matchBundles.get(i));
+ MatchInfo matchInfo = new MatchInfo(matchBundles.get(i), getGenericDocument());
mMatches.add(matchInfo);
}
}
@@ -124,13 +124,69 @@
/**
* Contains the database name that stored the {@link GenericDocument}.
*
- * @return Database name that stored the document
+ * @return Name of the database within which the document is stored
*/
@NonNull
public String getDatabaseName() {
return Preconditions.checkNotNull(mBundle.getString(DATABASE_NAME_FIELD));
}
+ /** Builder for {@link SearchResult} objects. */
+ public static final class Builder {
+ private final Bundle mBundle = new Bundle();
+ private final ArrayList<Bundle> mMatchInfos = new ArrayList<>();
+
+ private boolean mBuilt;
+
+ /**
+ * Constructs a new builder for {@link SearchResult} objects.
+ *
+ * @param packageName the package name the matched document belongs to
+ * @param databaseName the database name the matched document belongs to.
+ */
+ public Builder(@NonNull String packageName, @NonNull String databaseName) {
+ mBundle.putString(PACKAGE_NAME_FIELD, Preconditions.checkNotNull(packageName));
+ mBundle.putString(DATABASE_NAME_FIELD, Preconditions.checkNotNull(databaseName));
+ }
+
+ /**
+ * Sets the document which matched.
+ *
+ * @throws IllegalStateException if the builder has already been used
+ */
+ @NonNull
+ public Builder setGenericDocument(@NonNull GenericDocument document) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mBundle.putBundle(DOCUMENT_FIELD, document.getBundle());
+ return this;
+ }
+
+ /** Adds another match to this SearchResult. */
+ @NonNull
+ public Builder addMatch(@NonNull MatchInfo matchInfo) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ Preconditions.checkState(
+ matchInfo.mDocument == null,
+ "This MatchInfo is already associated with a SearchResult and can't be "
+ + "reassigned");
+ mMatchInfos.add(matchInfo.mBundle);
+ return this;
+ }
+
+ /**
+ * Constructs a new {@link SearchResult}.
+ *
+ * @throws IllegalStateException if the builder has already been used
+ */
+ @NonNull
+ public SearchResult build() {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mBundle.putParcelableArrayList(MATCHES_FIELD, mMatchInfos);
+ mBuilt = true;
+ return new SearchResult(mBundle);
+ }
+ }
+
/**
* 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
@@ -147,11 +203,11 @@
* <p>{@link MatchInfo#getFullText()} returns "A commonly used fake word is foo. Another
* nonsense word that’s used a lot is bar."
*
- * <p>{@link MatchInfo#getExactMatchPosition()} returns [29, 32]
+ * <p>{@link MatchInfo#getExactMatchRange()} returns [29, 32]
*
* <p>{@link MatchInfo#getExactMatch()} returns "foo"
*
- * <p>{@link MatchInfo#getSnippetPosition()} returns [26, 33]
+ * <p>{@link MatchInfo#getSnippetRange()} returns [26, 33]
*
* <p>{@link MatchInfo#getSnippet()} returns "is foo."
*
@@ -172,11 +228,11 @@
*
* <p>{@link MatchInfo#getFullText()} returns "Test Name Jr."
*
- * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 4]
+ * <p>{@link MatchInfo#getExactMatchRange()} returns [0, 4]
*
* <p>{@link MatchInfo#getExactMatch()} returns "Test"
*
- * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 9]
+ * <p>{@link MatchInfo#getSnippetRange()} returns [0, 9]
*
* <p>{@link MatchInfo#getSnippet()} returns "Test Name"
*
@@ -186,52 +242,54 @@
*
* <p>{@link MatchInfo#getFullText()} returns "TestNameJr@gmail.com"
*
- * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 20]
+ * <p>{@link MatchInfo#getExactMatchRange()} returns [0, 20]
*
* <p>{@link MatchInfo#getExactMatch()} returns "TestNameJr@gmail.com"
*
- * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 20]
+ * <p>{@link MatchInfo#getSnippetRange()} returns [0, 20]
*
* <p>{@link MatchInfo#getSnippet()} returns "TestNameJr@gmail.com"
*/
public static final class MatchInfo {
- /**
- * The path of the matching snippet property.
- *
- * @hide
- */
- public static final String PROPERTY_PATH_FIELD = "propertyPath";
+ /** The path of the matching snippet property. */
+ private static final String PROPERTY_PATH_FIELD = "propertyPath";
- /** @hide */
- public static final String EXACT_MATCH_POSITION_LOWER_FIELD = "exactMatchPositionLower";
+ private static final String EXACT_MATCH_RANGE_LOWER_FIELD = "exactMatchRangeLower";
+ private static final String EXACT_MATCH_RANGE_UPPER_FIELD = "exactMatchRangeUpper";
+ private static final String SNIPPET_RANGE_LOWER_FIELD = "snippetRangeLower";
+ private static final String SNIPPET_RANGE_UPPER_FIELD = "snippetRangeUpper";
- /** @hide */
- public static final String EXACT_MATCH_POSITION_UPPER_FIELD = "exactMatchPositionUpper";
-
- /** @hide */
- public static final String WINDOW_POSITION_LOWER_FIELD = "windowPositionLower";
-
- /** @hide */
- public static final String WINDOW_POSITION_UPPER_FIELD = "windowPositionUpper";
-
- private final String mFullText;
private final String mPropertyPath;
- private final Bundle mBundle;
- private MatchRange mExactMatchRange;
- private MatchRange mWindowRange;
+ final Bundle mBundle;
- MatchInfo(@NonNull GenericDocument document, @NonNull Bundle bundle) {
+ /**
+ * Document which the match comes from.
+ *
+ * <p>If this is {@code null}, methods which require access to the document, like {@link
+ * #getExactMatch}, will throw {@link NullPointerException}.
+ */
+ @Nullable final GenericDocument mDocument;
+
+ /** Full text of the matched property. Populated on first use. */
+ @Nullable private String mFullText;
+
+ /** Range of property that exactly matched the query. Populated on first use. */
+ @Nullable private MatchRange mExactMatchRange;
+
+ /** Range of some reasonable amount of context around the query. Populated on first use. */
+ @Nullable private MatchRange mWindowRange;
+
+ MatchInfo(@NonNull Bundle bundle, @Nullable GenericDocument document) {
mBundle = Preconditions.checkNotNull(bundle);
- Preconditions.checkNotNull(document);
+ mDocument = document;
mPropertyPath = Preconditions.checkNotNull(bundle.getString(PROPERTY_PATH_FIELD));
- mFullText = getPropertyValues(document, mPropertyPath);
}
/**
* Gets the property path corresponding to the given entry.
*
- * <p>Property Path: '.' - delimited sequence of property names indicating which property in
- * the Document these snippets correspond to.
+ * <p>A property path is a '.' - delimited sequence of property names indicating which
+ * property in the document these snippets correspond to.
*
* <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc. For class
* example 1 this returns "subject"
@@ -249,21 +307,34 @@
*/
@NonNull
public String getFullText() {
+ if (mFullText == null) {
+ Preconditions.checkState(
+ mDocument != null,
+ "Document has not been populated; this MatchInfo cannot be used yet");
+ mFullText = getPropertyValues(mDocument, mPropertyPath);
+ }
return mFullText;
}
+ /** @deprecated TODO(b/181887768): This method exists only for dogfooder transition. */
+ @NonNull
+ @Deprecated
+ public MatchRange getExactMatchPosition() {
+ return getExactMatchRange();
+ }
+
/**
* Gets the exact {@link MatchRange} corresponding to the given entry.
*
* <p>For class example 1 this returns [29, 32]
*/
@NonNull
- public MatchRange getExactMatchPosition() {
+ public MatchRange getExactMatchRange() {
if (mExactMatchRange == null) {
mExactMatchRange =
new MatchRange(
- mBundle.getInt(EXACT_MATCH_POSITION_LOWER_FIELD),
- mBundle.getInt(EXACT_MATCH_POSITION_UPPER_FIELD));
+ mBundle.getInt(EXACT_MATCH_RANGE_LOWER_FIELD),
+ mBundle.getInt(EXACT_MATCH_RANGE_UPPER_FIELD));
}
return mExactMatchRange;
}
@@ -275,7 +346,14 @@
*/
@NonNull
public CharSequence getExactMatch() {
- return getSubstring(getExactMatchPosition());
+ return getSubstring(getExactMatchRange());
+ }
+
+ /** @deprecated TODO(b/181887768): This method exists only for dogfooder transition. */
+ @NonNull
+ @Deprecated
+ public MatchRange getSnippetPosition() {
+ return getSnippetRange();
}
/**
@@ -287,12 +365,12 @@
* <p>For class example 1 this returns [29, 41].
*/
@NonNull
- public MatchRange getSnippetPosition() {
+ public MatchRange getSnippetRange() {
if (mWindowRange == null) {
mWindowRange =
new MatchRange(
- mBundle.getInt(WINDOW_POSITION_LOWER_FIELD),
- mBundle.getInt(WINDOW_POSITION_UPPER_FIELD));
+ mBundle.getInt(SNIPPET_RANGE_LOWER_FIELD),
+ mBundle.getInt(SNIPPET_RANGE_UPPER_FIELD));
}
return mWindowRange;
}
@@ -309,7 +387,7 @@
*/
@NonNull
public CharSequence getSnippet() {
- return getSubstring(getSnippetPosition());
+ return getSubstring(getSnippetRange());
}
private CharSequence getSubstring(MatchRange range) {
@@ -331,6 +409,72 @@
// TODO(b/175146044): Return the proper match based on the index in the propertyName.
return values[0];
}
+
+ /** Builder for {@link MatchInfo} objects. */
+ public static final class Builder {
+ private final Bundle mBundle = new Bundle();
+ private boolean mBuilt = false;
+
+ /**
+ * Sets the property path corresponding to the given entry.
+ *
+ * <p>A property path is a '.' - delimited sequence of property names indicating which
+ * property in the document these snippets correspond to.
+ *
+ * <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc. For class
+ * example 1 this returns "subject"
+ *
+ * @throws IllegalStateException if the builder has already been used
+ */
+ @NonNull
+ public Builder setPropertyPath(@NonNull String propertyPath) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mBundle.putString(
+ SearchResult.MatchInfo.PROPERTY_PATH_FIELD,
+ Preconditions.checkNotNull(propertyPath));
+ return this;
+ }
+
+ /**
+ * Sets the exact {@link MatchRange} corresponding to the given entry.
+ *
+ * @throws IllegalStateException if the builder has already been used
+ */
+ @NonNull
+ public Builder setExactMatchRange(@NonNull MatchRange matchRange) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ Preconditions.checkNotNull(matchRange);
+ mBundle.putInt(MatchInfo.EXACT_MATCH_RANGE_LOWER_FIELD, matchRange.getStart());
+ mBundle.putInt(MatchInfo.EXACT_MATCH_RANGE_UPPER_FIELD, matchRange.getEnd());
+ return this;
+ }
+
+ /**
+ * Sets the snippet {@link MatchRange} corresponding to the given entry.
+ *
+ * @throws IllegalStateException if the builder has already been used
+ */
+ @NonNull
+ public Builder setSnippetRange(@NonNull MatchRange matchRange) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ Preconditions.checkNotNull(matchRange);
+ mBundle.putInt(MatchInfo.SNIPPET_RANGE_LOWER_FIELD, matchRange.getStart());
+ mBundle.putInt(MatchInfo.SNIPPET_RANGE_UPPER_FIELD, matchRange.getEnd());
+ return this;
+ }
+
+ /**
+ * Constructs a new {@link MatchInfo}.
+ *
+ * @throws IllegalStateException if the builder has already been used
+ */
+ @NonNull
+ public MatchInfo build() {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mBuilt = true;
+ return new MatchInfo(mBundle, /*document=*/ null);
+ }
+ }
}
/**
@@ -353,7 +497,6 @@
*
* @param start The start point (inclusive)
* @param end The end point (exclusive)
- * @hide
*/
public MatchRange(int start, int end) {
if (start > end) {
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
index a146006..98cd49b 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
@@ -160,12 +160,8 @@
.addMigrationFailures(mMigrationFailures);
}
- /**
- * Builder for {@link SetSchemaResponse} objects.
- *
- * @hide
- */
- public static class Builder {
+ /** Builder for {@link SetSchemaResponse} objects. */
+ public static final class Builder {
private final ArrayList<MigrationFailure> mMigrationFailures = new ArrayList<>();
private final ArrayList<String> mDeletedTypes = new ArrayList<>();
private final ArrayList<String> mMigratedTypes = new ArrayList<>();
@@ -309,12 +305,8 @@
mBundle.getString(ERROR_MESSAGE_FIELD, /*defaultValue=*/ ""));
}
- /**
- * Builder for {@link MigrationFailure} objects.
- *
- * @hide
- */
- public static class Builder {
+ /** Builder for {@link MigrationFailure} objects. */
+ public static final class Builder {
private String mSchemaType;
private String mNamespace;
private String mUri;
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java
index b1a33a4..ca4ea2b 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java
@@ -32,19 +32,27 @@
/**
* Initializes an {@link AppSearchException} with no message.
*
- * @hide
+ * @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}.
*/
public AppSearchException(@AppSearchResult.ResultCode int resultCode) {
this(resultCode, /*message=*/ null);
}
- /** @hide */
+ /**
+ * Initializes an {@link AppSearchException} with a result code and message.
+ *
+ * @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}.
+ */
public AppSearchException(
@AppSearchResult.ResultCode int resultCode, @Nullable String message) {
this(resultCode, message, /*cause=*/ null);
}
- /** @hide */
+ /**
+ * Initializes an {@link AppSearchException} with a result code, message and cause.
+ *
+ * @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}.
+ */
public AppSearchException(
@AppSearchResult.ResultCode int resultCode,
@Nullable String message,
@@ -53,12 +61,16 @@
mResultCode = resultCode;
}
- /** Returns the result code this exception was constructed with. */
+ /**
+ * Returns the result code this exception was constructed with.
+ *
+ * @return One of the constants documented in {@link AppSearchResult#getResultCode}.
+ */
public @AppSearchResult.ResultCode int getResultCode() {
return mResultCode;
}
- /** Converts this {@link java.lang.Exception} into a failed {@link AppSearchResult} */
+ /** Converts this {@link java.lang.Exception} into a failed {@link AppSearchResult}. */
@NonNull
public <T> AppSearchResult<T> toAppSearchResult() {
return AppSearchResult.newFailedResult(mResultCode, getMessage());
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java
index a7f1cc4..4b8ce6d 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java
@@ -93,7 +93,7 @@
migrator.transform(
currentVersion,
finalVersion,
- searchResultPage.getResults().get(i).getDocument());
+ searchResultPage.getResults().get(i).getGenericDocument());
Bundle bundle = newDocument.getBundle();
Parcel parcel = Parcel.obtain();
parcel.writeBundle(bundle);
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java
index a2386ec..d6b9da8 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java
@@ -102,8 +102,8 @@
public static GenericDocument toGenericDocument(@NonNull DocumentProto proto) {
Preconditions.checkNotNull(proto);
GenericDocument.Builder<?> documentBuilder =
- new GenericDocument.Builder<>(proto.getUri(), proto.getSchema())
- .setNamespace(proto.getNamespace())
+ new GenericDocument.Builder<>(
+ proto.getNamespace(), proto.getUri(), proto.getSchema())
.setScore(proto.getScore())
.setTtlMillis(proto.getTtlMs())
.setCreationTimestampMillis(proto.getCreationTimestampMs());
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 e9852aa..1d8db72 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
@@ -58,14 +58,14 @@
@NonNull List<String> databaseNames) {
Preconditions.checkArgument(
proto.getResultsCount() == packageNames.size(),
- "Size of " + "results does not match the number of package names.");
+ "Size of results does not match the number of package names.");
Bundle bundle = new Bundle();
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), databaseNames.get(i)));
+ SearchResult result =
+ toSearchResult(proto.getResults(i), packageNames.get(i), databaseNames.get(i));
+ resultBundles.add(result.getBundle());
}
bundle.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles);
return new SearchResultPage(bundle);
@@ -80,50 +80,41 @@
* @return A {@link SearchResult} bundle.
*/
@NonNull
- private static Bundle toSearchResultBundle(
+ private static SearchResult toSearchResult(
@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<>();
+ SearchResult.Builder builder =
+ new SearchResult.Builder(packageName, databaseName).setGenericDocument(document);
if (proto.hasSnippet()) {
for (int i = 0; i < proto.getSnippet().getEntriesCount(); i++) {
SnippetProto.EntryProto entry = proto.getSnippet().getEntries(i);
for (int j = 0; j < entry.getSnippetMatchesCount(); j++) {
- Bundle matchInfoBundle =
- convertToMatchInfoBundle(
- entry.getSnippetMatches(j), entry.getPropertyName());
- matchList.add(matchInfoBundle);
+ SearchResult.MatchInfo matchInfo =
+ toMatchInfo(entry.getSnippetMatches(j), entry.getPropertyName());
+ builder.addMatch(matchInfo);
}
}
}
- bundle.putParcelableArrayList(SearchResult.MATCHES_FIELD, matchList);
-
- return bundle;
+ return builder.build();
}
- private static Bundle convertToMatchInfoBundle(
- SnippetMatchProto snippetMatchProto, String propertyPath) {
- Bundle bundle = new Bundle();
- bundle.putString(SearchResult.MatchInfo.PROPERTY_PATH_FIELD, propertyPath);
- bundle.putInt(
- SearchResult.MatchInfo.EXACT_MATCH_POSITION_LOWER_FIELD,
- snippetMatchProto.getExactMatchPosition());
- bundle.putInt(
- SearchResult.MatchInfo.EXACT_MATCH_POSITION_UPPER_FIELD,
- snippetMatchProto.getExactMatchPosition() + snippetMatchProto.getExactMatchBytes());
- bundle.putInt(
- SearchResult.MatchInfo.WINDOW_POSITION_LOWER_FIELD,
- snippetMatchProto.getWindowPosition());
- bundle.putInt(
- SearchResult.MatchInfo.WINDOW_POSITION_UPPER_FIELD,
- snippetMatchProto.getWindowPosition() + snippetMatchProto.getWindowBytes());
- return bundle;
+ private static SearchResult.MatchInfo toMatchInfo(
+ @NonNull SnippetMatchProto snippetMatchProto, @NonNull String propertyPath) {
+ return new SearchResult.MatchInfo.Builder()
+ .setPropertyPath(propertyPath)
+ .setExactMatchRange(
+ new SearchResult.MatchRange(
+ snippetMatchProto.getExactMatchPosition(),
+ snippetMatchProto.getExactMatchPosition()
+ + snippetMatchProto.getExactMatchBytes()))
+ .setSnippetRange(
+ new SearchResult.MatchRange(
+ snippetMatchProto.getWindowPosition(),
+ snippetMatchProto.getWindowPosition()
+ + snippetMatchProto.getWindowBytes()))
+ .build();
}
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
new file mode 100644
index 0000000..aeb66d9
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appsearch.stats;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Process;
+import android.os.SystemClock;
+import android.util.ArrayMap;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+import com.android.server.appsearch.external.localstorage.AppSearchLogger;
+import com.android.server.appsearch.external.localstorage.stats.CallStats;
+import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
+
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * Logger Implementation using Westworld.
+ *
+ * <p>This class is thread-safe.
+ *
+ * @hide
+ */
+public final class PlatformLogger implements AppSearchLogger {
+ private static final String TAG = "AppSearchPlatformLogger";
+
+ // Context of the system service.
+ private final Context mContext;
+
+ // User ID of the caller who we're logging for.
+ private final int mUserId;
+
+ // Configuration for the logger
+ private final Config mConfig;
+
+ private final Random mRng = new Random();
+ private final Object mLock = new Object();
+
+ /**
+ * SparseArray to track how many stats we skipped due to
+ * {@link Config#mMinTimeIntervalBetweenSamplesMillis}.
+ *
+ * <p> We can have correct extrapolated number by adding those counts back when we log
+ * the same type of stats next time. E.g. the true count of an event could be estimated as:
+ * SUM(sampling_ratio * (num_skipped_sample + 1)) as est_count
+ *
+ * <p>The key to the SparseArray is {@link CallStats.CallType}
+ */
+ @GuardedBy("mLock")
+ private final SparseIntArray mSkippedSampleCountLocked =
+ new SparseIntArray();
+
+ /**
+ * Map to cache the packageUid for each package.
+ *
+ * <p>It maps packageName to packageUid.
+ *
+ * <p>The entry will be removed whenever the app gets uninstalled
+ */
+ @GuardedBy("mLock")
+ private final Map<String, Integer> mPackageUidCacheLocked =
+ new ArrayMap<>();
+
+ /**
+ * Elapsed time for last stats logged from boot in millis
+ */
+ @GuardedBy("mLock")
+ private long mLastPushTimeMillisLocked = 0;
+
+ /**
+ * Class to configure the {@link PlatformLogger}
+ */
+ public static final class Config {
+ // Minimum time interval (in millis) since last message logged to Westworld before
+ // logging again.
+ private final long mMinTimeIntervalBetweenSamplesMillis;
+
+ // Default sampling ratio for all types of stats
+ private final int mDefaultSamplingRatio;
+
+ /**
+ * Sampling ratios for different types of stats
+ *
+ * <p>This SparseArray is passed by client and is READ-ONLY. The key to that SparseArray is
+ * {@link CallStats.CallType}
+ *
+ * <p>If sampling ratio is missing for certain stats type,
+ * {@link Config#mDefaultSamplingRatio} will be used.
+ *
+ * <p>E.g. sampling ratio=10 means that one out of every 10 stats was logged. If sampling
+ * ratio is 1, we will log each sample and it acts as if the sampling is disabled.
+ */
+ @NonNull
+ private final SparseIntArray mSamplingRatios;
+
+ /**
+ * Configuration for {@link PlatformLogger}
+ *
+ * @param minTimeIntervalBetweenSamplesMillis minimum time interval apart in Milliseconds
+ * required for two consecutive stats logged
+ * @param defaultSamplingRatio default sampling ratio
+ * @param samplingRatios SparseArray to customize sampling ratio for
+ * different stat types
+ */
+ public Config(long minTimeIntervalBetweenSamplesMillis,
+ int defaultSamplingRatio,
+ @Nullable SparseIntArray samplingRatios) {
+ mMinTimeIntervalBetweenSamplesMillis = minTimeIntervalBetweenSamplesMillis;
+ mDefaultSamplingRatio = defaultSamplingRatio;
+ if (samplingRatios != null) {
+ mSamplingRatios = samplingRatios;
+ } else {
+ mSamplingRatios = new SparseIntArray();
+ }
+ }
+ }
+
+ /**
+ * Helper class to hold platform specific stats for Westworld.
+ */
+ static final class ExtraStats {
+ // UID for the calling package of the stats.
+ final int mPackageUid;
+ // sampling ratio for the call type of the stats.
+ final int mSamplingRatio;
+ // number of samplings skipped before the current one for the same call type.
+ final int mSkippedSampleCount;
+
+ ExtraStats(int packageUid, int samplingRatio, int skippedSampleCount) {
+ mPackageUid = packageUid;
+ mSamplingRatio = samplingRatio;
+ mSkippedSampleCount = skippedSampleCount;
+ }
+ }
+
+ /**
+ * Westworld constructor
+ */
+ public PlatformLogger(@NonNull Context context, int userId, @NonNull Config config) {
+ mContext = Preconditions.checkNotNull(context);
+ mConfig = Preconditions.checkNotNull(config);
+ mUserId = userId;
+ }
+
+ /** Logs {@link CallStats}. */
+ @Override
+ public void logStats(@NonNull CallStats stats) {
+ Preconditions.checkNotNull(stats);
+ synchronized (mLock) {
+ if (shouldLogForTypeLocked(stats.getCallType())) {
+ logToWestworldLocked(stats);
+ }
+ }
+ }
+
+ /** Logs {@link PutDocumentStats}. */
+ @Override
+ public void logStats(@NonNull PutDocumentStats stats) {
+ Preconditions.checkNotNull(stats);
+ synchronized (mLock) {
+ if (shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)) {
+ logToWestworldLocked(stats);
+ }
+ }
+ }
+
+ /**
+ * Removes cached UID for package.
+ *
+ * @return removed UID for the package, or {@code INVALID_UID} if package was not previously
+ * cached.
+ */
+ public int removeCachedUidForPackage(@NonNull String packageName) {
+ // TODO(b/173532925) This needs to be called when we get PACKAGE_REMOVED intent
+ Preconditions.checkNotNull(packageName);
+ synchronized (mLock) {
+ Integer uid = mPackageUidCacheLocked.remove(packageName);
+ return uid != null ? uid : Process.INVALID_UID;
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void logToWestworldLocked(@NonNull CallStats stats) {
+ mLastPushTimeMillisLocked = SystemClock.elapsedRealtime();
+ ExtraStats extraStats = createExtraStatsLocked(stats.getGeneralStats().getPackageName(),
+ stats.getCallType());
+ /* TODO(b/173532925) Log the CallStats to Westworld
+ stats.log(..., samplingRatio, skippedSampleCount, ...)
+ */
+ }
+
+ @GuardedBy("mLock")
+ private void logToWestworldLocked(@NonNull PutDocumentStats stats) {
+ mLastPushTimeMillisLocked = SystemClock.elapsedRealtime();
+ ExtraStats extraStats = createExtraStatsLocked(stats.getGeneralStats().getPackageName(),
+ CallStats.CALL_TYPE_PUT_DOCUMENT);
+ /* TODO(b/173532925) Log the PutDocumentStats to Westworld
+ stats.log(..., samplingRatio, skippedSampleCount, ...)
+ */
+ }
+
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ @NonNull
+ ExtraStats createExtraStatsLocked(@NonNull String packageName,
+ @CallStats.CallType int callType) {
+ int packageUid = getPackageUidAsUserLocked(packageName);
+ int samplingRatio = mConfig.mSamplingRatios.get(callType,
+ mConfig.mDefaultSamplingRatio);
+
+ int skippedSampleCount = mSkippedSampleCountLocked.get(callType,
+ /*valueOfKeyIfNotFound=*/ 0);
+ mSkippedSampleCountLocked.put(callType, 0);
+
+ return new ExtraStats(packageUid, samplingRatio, skippedSampleCount);
+ }
+
+ /**
+ * Checks if this stats should be logged.
+ *
+ * <p>It won't be logged if it is "sampled" out, or it is too close to the previous logged
+ * stats.
+ */
+ @GuardedBy("mLock")
+ @VisibleForTesting
+ boolean shouldLogForTypeLocked(@CallStats.CallType int callType) {
+ int samplingRatio = mConfig.mSamplingRatios.get(callType,
+ mConfig.mDefaultSamplingRatio);
+
+ // Sampling
+ if (!shouldSample(samplingRatio)) {
+ return false;
+ }
+
+ // Rate limiting
+ // Check the timestamp to see if it is too close to last logged sample
+ long currentTimeMillis = SystemClock.elapsedRealtime();
+ if (mLastPushTimeMillisLocked
+ > currentTimeMillis - mConfig.mMinTimeIntervalBetweenSamplesMillis) {
+ int count = mSkippedSampleCountLocked.get(callType, /*valueOfKeyIfNotFound=*/ 0);
+ ++count;
+ mSkippedSampleCountLocked.put(callType, count);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Checks if the stats should be "sampled"
+ *
+ * @param samplingRatio sampling ratio
+ * @return if the stats should be sampled
+ */
+ private boolean shouldSample(int samplingRatio) {
+ if (samplingRatio <= 0) {
+ return false;
+ }
+
+ return mRng.nextInt((int) samplingRatio) == 0;
+ }
+
+ /**
+ * Finds the UID of the {@code packageName}. Returns {@link Process#INVALID_UID} if unable to
+ * find the UID.
+ */
+ @GuardedBy("mLock")
+ private int getPackageUidAsUserLocked(@NonNull String packageName) {
+ Integer packageUid = mPackageUidCacheLocked.get(packageName);
+ if (packageUid != null) {
+ return packageUid;
+ }
+
+ // TODO(b/173532925) since VisibilityStore has the same method, we can make this a
+ // utility function
+ try {
+ packageUid = mContext.getPackageManager().getPackageUidAsUser(packageName, mUserId);
+ mPackageUidCacheLocked.put(packageName, packageUid);
+ return packageUid;
+ } catch (PackageManager.NameNotFoundException e) {
+ // Package doesn't exist, continue
+ }
+ return Process.INVALID_UID;
+ }
+
+ //
+ // Functions below are used for tests only
+ //
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ void setLastPushTimeMillisLocked(long lastPushElapsedTimeMillis) {
+ mLastPushTimeMillisLocked = lastPushElapsedTimeMillis;
+ }
+}
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index 41c70f0..68531b6 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-I42b89416968565ceb6483b400894f5b49524208c
+I1926fb1d13628607f7a513c8149b65dd86c98dd6
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
index 37717d6..4a3c7a5 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
@@ -47,10 +47,7 @@
AppSearchBatchResult<String, GenericDocument> result =
checkIsBatchResultSuccess(
session.getByUri(
- new GetByUriRequest.Builder()
- .setNamespace(namespace)
- .addUris(uris)
- .build()));
+ new GetByUriRequest.Builder(namespace).addUris(uris).build()));
assertThat(result.getSuccesses()).hasSize(uris.length);
assertThat(result.getFailures()).isEmpty();
List<GenericDocument> list = new ArrayList<>(uris.length);
@@ -80,7 +77,7 @@
List<GenericDocument> documents = new ArrayList<>();
while (results.size() > 0) {
for (SearchResult result : results) {
- documents.add(result.getDocument());
+ documents.add(result.getGenericDocument());
}
results = searchResults.getNextPage().get();
}
diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
index 77146e0..78c5b15 100644
--- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java
+++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
@@ -539,6 +539,11 @@
* scheduled as exact. Applications are strongly discouraged from using exact
* alarms unnecessarily as they reduce the OS's ability to minimize battery use.
*
+ * <p>
+ * Starting with {@link Build.VERSION_CODES#S}, apps require the
+ * {@link Manifest.permission#SCHEDULE_EXACT_ALARM SCHEDULE_EXACT_ALARM} permission to use this
+ * API.
+ *
* @param type type of alarm.
* @param triggerAtMillis time in milliseconds that the alarm should go
* off, using the appropriate clock (depending on the alarm type).
@@ -558,6 +563,7 @@
* @see #RTC
* @see #RTC_WAKEUP
*/
+ @RequiresPermission(value = Manifest.permission.SCHEDULE_EXACT_ALARM, conditional = true)
public void setExact(@AlarmType int type, long triggerAtMillis, PendingIntent operation) {
setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, operation, null, null, null,
null, null);
@@ -571,7 +577,13 @@
* The OnAlarmListener's {@link OnAlarmListener#onAlarm() onAlarm()} method will be
* invoked via the specified target Handler, or on the application's main looper
* if {@code null} is passed as the {@code targetHandler} parameter.
+ *
+ * <p>
+ * Starting with {@link Build.VERSION_CODES#S}, apps require the
+ * {@link Manifest.permission#SCHEDULE_EXACT_ALARM SCHEDULE_EXACT_ALARM} permission to use this
+ * API.
*/
+ @RequiresPermission(value = Manifest.permission.SCHEDULE_EXACT_ALARM, conditional = true)
public void setExact(@AlarmType int type, long triggerAtMillis, String tag,
OnAlarmListener listener, Handler targetHandler) {
setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, null, listener, tag,
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index d9a49aa..3515976 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -29,6 +29,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.UserHandleAware;
import android.content.Context;
import java.lang.annotation.Retention;
@@ -295,6 +296,11 @@
* @hide
*/
public static final int REASON_SHELL = 316;
+ /**
+ * Media session callbacks.
+ * @hide
+ */
+ public static final int REASON_MEDIA_SESSION_CALLBACK = 317;
/**
* The list of BG-FGS-Launch and temp-allow-list reason code.
@@ -354,6 +360,7 @@
REASON_EVENT_SMS,
REASON_EVENT_MMS,
REASON_SHELL,
+ REASON_MEDIA_SESSION_CALLBACK,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ReasonCode {}
@@ -451,6 +458,7 @@
* @param reasonCode one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure.
* @param reason a optional human readable reason string, could be null or empty string.
*/
+ @UserHandleAware
@RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
public void addToTemporaryAllowList(@NonNull String packageName, long durationMs,
@ReasonCode int reasonCode, @Nullable String reason) {
@@ -474,6 +482,7 @@
* used for logging purposes. Could be null or empty string.
* @return The duration (in milliseconds) that the app is allow-listed for
*/
+ @UserHandleAware
@RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
public long addToTemporaryAllowListForEvent(@NonNull String packageName,
@AllowListEvent int event, @ReasonCode int reasonCode, @Nullable String reason) {
@@ -626,6 +635,8 @@
return "EVENT_MMS";
case REASON_SHELL:
return "SHELL";
+ case REASON_MEDIA_SESSION_CALLBACK:
+ return "MEDIA_SESSION_CALLBACK";
default:
return "(unknown:" + reasonCode + ")";
}
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 119dcb6..667fc60 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -2692,7 +2692,7 @@
void addPowerSaveTempAllowlistAppChecked(String packageName, long duration,
int userId, @ReasonCode int reasonCode, @Nullable String reason)
throws RemoteException {
- getContext().enforceCallingPermission(
+ getContext().enforceCallingOrSelfPermission(
Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
"No permission to change device idle whitelist");
final int callingUid = Binder.getCallingUid();
@@ -2715,7 +2715,7 @@
void removePowerSaveTempAllowlistAppChecked(String packageName, int userId)
throws RemoteException {
- getContext().enforceCallingPermission(
+ getContext().enforceCallingOrSelfPermission(
Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
"No permission to change device idle whitelist");
final int callingUid = Binder.getCallingUid();
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 33f6e06..58fc874 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -388,6 +388,8 @@
@VisibleForTesting
static final String KEY_MAX_INTERVAL = "max_interval";
@VisibleForTesting
+ static final String KEY_MIN_WINDOW = "min_window";
+ @VisibleForTesting
static final String KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION
= "allow_while_idle_whitelist_duration";
@VisibleForTesting
@@ -428,11 +430,13 @@
@VisibleForTesting
static final String KEY_ALLOW_WHILE_IDLE_COMPAT_WINDOW = "allow_while_idle_compat_window";
- private static final String KEY_CRASH_NON_CLOCK_APPS = "crash_non_clock_apps";
+ @VisibleForTesting
+ static final String KEY_CRASH_NON_CLOCK_APPS = "crash_non_clock_apps";
private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
private static final long DEFAULT_MAX_INTERVAL = 365 * INTERVAL_DAY;
+ private static final long DEFAULT_MIN_WINDOW = 10_000;
private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10 * 1000;
private static final long DEFAULT_LISTENER_TIMEOUT = 5 * 1000;
private static final int DEFAULT_MAX_ALARMS_PER_UID = 500;
@@ -475,6 +479,9 @@
// Maximum alarm recurrence interval
public long MAX_INTERVAL = DEFAULT_MAX_INTERVAL;
+ // Minimum window size for inexact alarms
+ public long MIN_WINDOW = DEFAULT_MIN_WINDOW;
+
// BroadcastOptions.setTemporaryAppWhitelistDuration() to use for FLAG_ALLOW_WHILE_IDLE.
public long ALLOW_WHILE_IDLE_WHITELIST_DURATION
= DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
@@ -575,6 +582,9 @@
ALLOW_WHILE_IDLE_QUOTA = 1;
}
break;
+ case KEY_MIN_WINDOW:
+ MIN_WINDOW = properties.getLong(KEY_MIN_WINDOW, DEFAULT_MIN_WINDOW);
+ break;
case KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA:
ALLOW_WHILE_IDLE_COMPAT_QUOTA = properties.getInt(
KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA,
@@ -738,6 +748,11 @@
TimeUtils.formatDuration(MAX_INTERVAL, pw);
pw.println();
+ pw.print(KEY_MIN_WINDOW);
+ pw.print("=");
+ TimeUtils.formatDuration(MIN_WINDOW, pw);
+ pw.println();
+
pw.print(KEY_LISTENER_TIMEOUT);
pw.print("=");
TimeUtils.formatDuration(LISTENER_TIMEOUT, pw);
@@ -1642,6 +1657,7 @@
// Fix this window in place, so that as time approaches we don't collapse it.
windowLength = maxElapsed - triggerElapsed;
} else {
+ windowLength = Math.max(windowLength, mConstants.MIN_WINDOW);
maxElapsed = triggerElapsed + windowLength;
}
synchronized (mLock) {
@@ -1981,8 +1997,10 @@
* Returns true if the given uid does not require SCHEDULE_EXACT_ALARM to set exact,
* allow-while-idle alarms.
*/
- boolean isExemptFromPermission(int uid) {
- return (UserHandle.isSameApp(mSystemUiUid, uid) || mLocalDeviceIdleController == null
+ boolean isExemptFromExactAlarmPermission(int uid) {
+ return (UserHandle.isSameApp(mSystemUiUid, uid)
+ || UserHandle.isCore(uid)
+ || mLocalDeviceIdleController == null
|| mLocalDeviceIdleController.isAppOnWhitelist(UserHandle.getAppId(uid)));
}
@@ -2002,54 +2020,43 @@
mAppOps.checkPackage(callingUid, callingPackage);
final boolean allowWhileIdle = (flags & FLAG_ALLOW_WHILE_IDLE) != 0;
+ final boolean exact = (windowLength == AlarmManager.WINDOW_EXACT);
+ // make sure the caller is allowed to use the requested kind of alarm, and also
+ // decide what quota and broadcast options to use.
Bundle idleOptions = null;
- if (alarmClock != null || allowWhileIdle) {
- // make sure the caller is allowed to use the requested kind of alarm, and also
- // decide what broadcast options to use.
+ if (exact || allowWhileIdle) {
final boolean needsPermission;
- boolean lowQuota;
+ boolean lowerQuota;
if (CompatChanges.isChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION,
callingPackage, UserHandle.getUserHandleForUid(callingUid))) {
- if (windowLength != AlarmManager.WINDOW_EXACT) {
- needsPermission = false;
- lowQuota = true;
- idleOptions = isExemptFromPermission(callingUid) ? mOptsWithFgs.toBundle()
- : mOptsWithoutFgs.toBundle();
- } else if (alarmClock != null) {
- needsPermission = true;
- lowQuota = false;
- idleOptions = mOptsWithFgs.toBundle();
- } else {
- needsPermission = true;
- lowQuota = false;
- idleOptions = mOptsWithFgs.toBundle();
- }
+ needsPermission = exact;
+ lowerQuota = !exact;
+ idleOptions = exact ? mOptsWithFgs.toBundle() : mOptsWithoutFgs.toBundle();
} else {
needsPermission = false;
- lowQuota = allowWhileIdle;
+ lowerQuota = allowWhileIdle;
idleOptions = allowWhileIdle ? mOptsWithFgs.toBundle() : null;
}
if (needsPermission && !canScheduleExactAlarms()) {
- if (alarmClock == null && isExemptFromPermission(callingUid)) {
- // If the app is on the full system allow-list (not except-idle), we still
- // allow the alarms, but with a lower quota to keep pre-S compatibility.
- lowQuota = true;
- } else {
+ if (alarmClock != null || !isExemptFromExactAlarmPermission(callingUid)) {
final String errorMessage = "Caller " + callingPackage + " needs to hold "
+ Manifest.permission.SCHEDULE_EXACT_ALARM + " to set "
- + ((allowWhileIdle) ? "exact, allow-while-idle" : "alarm-clock")
- + " alarms.";
+ + "exact alarms.";
if (mConstants.CRASH_NON_CLOCK_APPS) {
throw new SecurityException(errorMessage);
} else {
Slog.wtf(TAG, errorMessage);
- idleOptions = mOptsWithoutFgs.toBundle();
- lowQuota = allowWhileIdle;
}
}
+ // If the app is on the full system power allow-list (not except-idle), or we're
+ // in a soft failure mode, we still allow the alarms.
+ // We give temporary allowlist to allow-while-idle alarms but without FGS
+ // capability. Note that apps that are in the power allow-list do not need it.
+ idleOptions = allowWhileIdle ? mOptsWithoutFgs.toBundle() : null;
+ lowerQuota = allowWhileIdle;
}
- if (lowQuota) {
+ if (lowerQuota) {
flags &= ~FLAG_ALLOW_WHILE_IDLE;
flags |= FLAG_ALLOW_WHILE_IDLE_COMPAT;
}
@@ -2998,13 +3005,10 @@
/**
* Called when an app loses {@link Manifest.permission#SCHEDULE_EXACT_ALARM} to remove alarms
* that the app is no longer eligible to use.
- * TODO (b/179541791): Revisit and write tests once UX is final.
+ * TODO (b/179541791): Add revocation history to dumpsys.
*/
void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName) {
- if (UserHandle.isCore(uid) || uid == mSystemUiUid) {
- return;
- }
- if (isExemptFromPermission(uid)) {
+ if (isExemptFromExactAlarmPermission(uid)) {
return;
}
if (!CompatChanges.isChangeEnabled(
@@ -3015,7 +3019,7 @@
final Predicate<Alarm> whichAlarms =
a -> (a.uid == uid && a.packageName.equals(packageName)
- && ((a.flags & FLAG_ALLOW_WHILE_IDLE) != 0 || a.alarmClock != null));
+ && a.windowLength == AlarmManager.WINDOW_EXACT);
final ArrayList<Alarm> removed = mAlarmStore.remove(whichAlarms);
final boolean didRemove = !removed.isEmpty();
if (didRemove) {
@@ -3873,6 +3877,7 @@
return alarm.creatorUid;
}
+
@VisibleForTesting
class AlarmHandler extends Handler {
public static final int ALARM_EVENT = 1;
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java
index 0e442d0..e684b84 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java
@@ -134,6 +134,11 @@
void dumpProto(ProtoOutputStream pos, long nowElapsed);
/**
+ * @return a name for this alarm store that can be used for debugging and tests.
+ */
+ String getName();
+
+ /**
* A functional interface used to update the alarm. Used to describe the update in
* {@link #updateAlarmDeliveries(AlarmDeliveryCalculator)}
*/
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java
index e7edfb7..cb528ba 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java
@@ -27,6 +27,7 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.StatLogger;
import java.text.SimpleDateFormat;
@@ -40,6 +41,8 @@
* This keeps the alarms in batches, which are sorted on the start time of their delivery window.
*/
public class BatchingAlarmStore implements AlarmStore {
+ @VisibleForTesting
+ static final String TAG = BatchingAlarmStore.class.getSimpleName();
private final ArrayList<Batch> mAlarmBatches = new ArrayList<>();
private int mSize;
@@ -49,7 +52,7 @@
int REBATCH_ALL_ALARMS = 0;
}
- final StatLogger mStatLogger = new StatLogger("BatchingAlarmStore stats", new String[]{
+ final StatLogger mStatLogger = new StatLogger(TAG + " stats", new String[]{
"REBATCH_ALL_ALARMS",
});
@@ -211,6 +214,11 @@
}
}
+ @Override
+ public String getName() {
+ return TAG;
+ }
+
private void insertAndBatchAlarm(Alarm alarm) {
final int whichBatch = ((alarm.flags & AlarmManager.FLAG_STANDALONE) != 0) ? -1
: attemptCoalesce(alarm.getWhenElapsed(), alarm.getMaxWhenElapsed());
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
index 8ca1446..c37d2c3 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
@@ -25,6 +25,7 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.StatLogger;
import java.text.SimpleDateFormat;
@@ -38,6 +39,8 @@
* This keeps the alarms in a sorted list, and only batches them at the time of delivery.
*/
public class LazyAlarmStore implements AlarmStore {
+ @VisibleForTesting
+ static final String TAG = LazyAlarmStore.class.getSimpleName();
private final ArrayList<Alarm> mAlarms = new ArrayList<>();
private Runnable mOnAlarmClockRemoved;
@@ -47,7 +50,7 @@
int GET_NEXT_WAKEUP_DELIVERY_TIME = 1;
}
- final StatLogger mStatLogger = new StatLogger("LazyAlarmStore stats", new String[]{
+ final StatLogger mStatLogger = new StatLogger(TAG + " stats", new String[]{
"GET_NEXT_DELIVERY_TIME",
"GET_NEXT_WAKEUP_DELIVERY_TIME",
});
@@ -214,4 +217,9 @@
a.dumpDebug(pos, AlarmManagerServiceDumpProto.PENDING_ALARMS, nowElapsed);
}
}
+
+ @Override
+ public String getName() {
+ return TAG;
+ }
}
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 b70e68b..2f3ac22 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
@@ -813,12 +813,21 @@
// 1. it's already running (already executing expedited jobs should be allowed to finish)
// 2. the app is currently in the foreground
// 3. the app overall is within its quota
+ // 4. It's on the temp allowlist (or within the grace period)
if (isTopStartedJobLocked(jobStatus) || isUidInForeground(jobStatus.getSourceUid())) {
return true;
}
+ final long tempAllowlistGracePeriodEndElapsed =
+ mTempAllowlistGraceCache.get(jobStatus.getSourceUid());
+ final boolean hasTempAllowlistExemption = mTempAllowlistCache.get(jobStatus.getSourceUid())
+ || sElapsedRealtimeClock.millis() < tempAllowlistGracePeriodEndElapsed;
+ if (hasTempAllowlistExemption) {
+ return true;
+ }
+
Timer ejTimer = mEJPkgTimers.get(jobStatus.getSourceUserId(),
jobStatus.getSourcePackageName());
- // Any already executing expedited jbos should be allowed to finish.
+ // Any already executing expedited jobs should be allowed to finish.
if (ejTimer != null && ejTimer.isRunning(jobStatus)) {
return true;
}
diff --git a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
index 906071f..3f30d3e 100644
--- a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
+++ b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
@@ -19,11 +19,14 @@
import android.annotation.NonNull;
import android.content.ContentResolver;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
+import com.android.modules.annotation.MinSdk;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -77,6 +80,7 @@
There are four types of HDR video(Dolby-Vision, HDR10, HDR10+, HLG) supported by the platform,
application will only need to specify individual types they supported.
*/
+@MinSdk(Build.VERSION_CODES.S)
public final class ApplicationMediaCapabilities implements Parcelable {
private static final String TAG = "ApplicationMediaCapabilities";
diff --git a/apex/media/framework/java/android/media/MediaFeature.java b/apex/media/framework/java/android/media/MediaFeature.java
index 0e461888..8d1b159 100644
--- a/apex/media/framework/java/android/media/MediaFeature.java
+++ b/apex/media/framework/java/android/media/MediaFeature.java
@@ -17,6 +17,9 @@
package android.media;
import android.annotation.StringDef;
+import android.os.Build;
+
+import com.android.modules.annotation.MinSdk;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -24,6 +27,7 @@
/**
* MediaFeature defines various media features, e.g. hdr type.
*/
+@MinSdk(Build.VERSION_CODES.S)
public final class MediaFeature {
/**
* Defines tye type of HDR(high dynamic range) video.
diff --git a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
index 9332835..de2924e 100644
--- a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
+++ b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
@@ -21,7 +21,9 @@
import android.annotation.SystemApi.Client;
import android.app.SystemServiceRegistry;
import android.content.Context;
+import android.os.Build;
+import com.android.modules.annotation.MinSdk;
import com.android.modules.utils.build.SdkLevel;
/**
@@ -29,6 +31,7 @@
*
* @hide
*/
+@MinSdk(Build.VERSION_CODES.S)
@SystemApi(client = Client.MODULE_LIBRARIES)
public class MediaFrameworkInitializer {
private MediaFrameworkInitializer() {
diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodeManager.java
index 30d1896..84332e5 100644
--- a/apex/media/framework/java/android/media/MediaTranscodeManager.java
+++ b/apex/media/framework/java/android/media/MediaTranscodeManager.java
@@ -26,6 +26,7 @@
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.net.Uri;
+import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -34,6 +35,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.annotation.MinSdk;
import java.io.FileNotFoundException;
import java.lang.annotation.Retention;
@@ -99,6 +101,7 @@
TODO(hkuang): Clarify whether supports framerate conversion.
@hide
*/
+@MinSdk(Build.VERSION_CODES.S)
@SystemApi
public final class MediaTranscodeManager {
private static final String TAG = "MediaTranscodeManager";
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index 260c8a4..f5bee6c 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -258,7 +258,7 @@
public void runDisableAppDataIsolation() throws RemoteException {
if (!SystemProperties.getBoolean(
- ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false)) {
+ ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true)) {
throw new IllegalStateException("Storage app data isolation is not enabled.");
}
final String pkgName = nextArg();
diff --git a/core/api/current.txt b/core/api/current.txt
index c0940d6..97f9855 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1394,6 +1394,7 @@
field public static final int supportsRtl = 16843695; // 0x10103af
field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb
field public static final int supportsUploading = 16843419; // 0x101029b
+ field public static final int suppressesSpellChecker = 16844354; // 0x1010642
field public static final int switchMinWidth = 16843632; // 0x1010370
field public static final int switchPadding = 16843633; // 0x1010371
field public static final int switchPreferenceStyle = 16843629; // 0x101036d
@@ -3026,6 +3027,7 @@
field public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON_CHOOSER = 12; // 0xc
field public static final int GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT = 13; // 0xd
field public static final int GLOBAL_ACTION_BACK = 1; // 0x1
+ field public static final int GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE = 15; // 0xf
field public static final int GLOBAL_ACTION_HOME = 2; // 0x2
field public static final int GLOBAL_ACTION_KEYCODE_HEADSETHOOK = 10; // 0xa
field public static final int GLOBAL_ACTION_LOCK_SCREEN = 8; // 0x8
@@ -4349,8 +4351,8 @@
method public void set(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler);
method @RequiresPermission(android.Manifest.permission.SCHEDULE_EXACT_ALARM) public void setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent);
method public void setAndAllowWhileIdle(int, long, android.app.PendingIntent);
- method public void setExact(int, long, android.app.PendingIntent);
- method public void setExact(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler);
+ method @RequiresPermission(value=android.Manifest.permission.SCHEDULE_EXACT_ALARM, conditional=true) public void setExact(int, long, android.app.PendingIntent);
+ method @RequiresPermission(value=android.Manifest.permission.SCHEDULE_EXACT_ALARM, conditional=true) public void setExact(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler);
method @RequiresPermission(value=android.Manifest.permission.SCHEDULE_EXACT_ALARM, conditional=true) public void setExactAndAllowWhileIdle(int, long, android.app.PendingIntent);
method public void setInexactRepeating(int, long, long, android.app.PendingIntent);
method public void setRepeating(int, long, long, android.app.PendingIntent);
@@ -9908,6 +9910,7 @@
method public String getHtmlText();
method public android.content.Intent getIntent();
method public CharSequence getText();
+ method @Nullable public android.view.textclassifier.TextLinks getTextLinks();
method public android.net.Uri getUri();
}
@@ -9917,6 +9920,8 @@
method public static boolean compareMimeTypes(String, String);
method public int describeContents();
method public String[] filterMimeTypes(String);
+ method public int getClassificationStatus();
+ method @FloatRange(from=0.0, to=1.0) public float getConfidenceScore(@NonNull String);
method public android.os.PersistableBundle getExtras();
method public CharSequence getLabel();
method public String getMimeType(int);
@@ -9926,6 +9931,9 @@
method public boolean isStyledText();
method public void setExtras(android.os.PersistableBundle);
method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CLASSIFICATION_COMPLETE = 3; // 0x3
+ field public static final int CLASSIFICATION_NOT_COMPLETE = 1; // 0x1
+ field public static final int CLASSIFICATION_NOT_PERFORMED = 2; // 0x2
field @NonNull public static final android.os.Parcelable.Creator<android.content.ClipDescription> CREATOR;
field public static final String MIMETYPE_TEXT_HTML = "text/html";
field public static final String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";
@@ -10953,6 +10961,7 @@
field public static final String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED";
field public static final String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
field public static final String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
+ field public static final String ACTION_MANAGE_UNUSED_APPS = "android.intent.action.MANAGE_UNUSED_APPS";
field public static final String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL";
field public static final String ACTION_MEDIA_BUTTON = "android.intent.action.MEDIA_BUTTON";
field public static final String ACTION_MEDIA_CHECKING = "android.intent.action.MEDIA_CHECKING";
@@ -21890,6 +21899,7 @@
method @NonNull public java.util.List<byte[]> getOfflineLicenseKeySetIds();
method public int getOfflineLicenseState(@NonNull byte[]);
method public int getOpenSessionCount();
+ method @Nullable public android.media.metrics.PlaybackComponent getPlaybackComponent(@NonNull byte[]);
method @NonNull public byte[] getPropertyByteArray(String);
method @NonNull public String getPropertyString(@NonNull String);
method @NonNull public android.media.MediaDrm.ProvisionRequest getProvisionRequest();
@@ -36863,11 +36873,12 @@
method @NonNull public static android.content.Intent createInstallIntent();
method @NonNull public static android.content.Intent createManageCredentialsIntent(@NonNull android.security.AppUriAuthenticationPolicy);
method @Nullable @WorkerThread public static java.security.cert.X509Certificate[] getCertificateChain(@NonNull android.content.Context, @NonNull String) throws java.lang.InterruptedException, android.security.KeyChainException;
- method @NonNull public static android.security.AppUriAuthenticationPolicy getCredentialManagementAppPolicy(@NonNull android.content.Context) throws java.lang.SecurityException;
+ method @NonNull @WorkerThread public static android.security.AppUriAuthenticationPolicy getCredentialManagementAppPolicy(@NonNull android.content.Context) throws java.lang.SecurityException;
method @Nullable @WorkerThread public static java.security.PrivateKey getPrivateKey(@NonNull android.content.Context, @NonNull String) throws java.lang.InterruptedException, android.security.KeyChainException;
method @Deprecated public static boolean isBoundKeyAlgorithm(@NonNull String);
- method public static boolean isCredentialManagementApp(@NonNull android.content.Context);
+ method @WorkerThread public static boolean isCredentialManagementApp(@NonNull android.content.Context);
method public static boolean isKeyAlgorithmSupported(@NonNull String);
+ method @RequiresPermission(value="android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP", conditional=true) @WorkerThread public static boolean removeCredentialManagementApp(@NonNull android.content.Context);
field public static final String ACTION_KEYCHAIN_CHANGED = "android.security.action.KEYCHAIN_CHANGED";
field public static final String ACTION_KEY_ACCESS_CHANGED = "android.security.action.KEY_ACCESS_CHANGED";
field @Deprecated public static final String ACTION_STORAGE_CHANGED = "android.security.STORAGE_CHANGED";
@@ -49878,6 +49889,7 @@
public interface WindowManager extends android.view.ViewManager {
method public default void addCrossWindowBlurEnabledListener(@NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method public default void addCrossWindowBlurEnabledListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @NonNull public default android.view.WindowMetrics getCurrentWindowMetrics();
method @Deprecated public android.view.Display getDefaultDisplay();
method @NonNull public default android.view.WindowMetrics getMaximumWindowMetrics();
@@ -51469,6 +51481,7 @@
method public int getSubtypeCount();
method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
method public CharSequence loadLabel(android.content.pm.PackageManager);
+ method public boolean suppressesSpellChecker();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.InputMethodInfo> CREATOR;
}
@@ -51490,6 +51503,7 @@
method public boolean isActive(android.view.View);
method public boolean isActive();
method public boolean isFullscreenMode();
+ method public boolean isInputMethodSuppressingSpellChecker();
method @Deprecated public boolean isWatchingCursor(android.view.View);
method public void restartInput(android.view.View);
method public void sendAppPrivateCommand(android.view.View, String, android.os.Bundle);
@@ -51910,6 +51924,7 @@
field public static final String TYPE_PHONE = "phone";
field public static final String TYPE_UNKNOWN = "";
field public static final String TYPE_URL = "url";
+ field public static final String WIDGET_TYPE_CLIPBOARD = "clipboard";
field public static final String WIDGET_TYPE_CUSTOM_EDITTEXT = "customedit";
field public static final String WIDGET_TYPE_CUSTOM_TEXTVIEW = "customview";
field public static final String WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
@@ -52386,6 +52401,17 @@
method @Nullable @WorkerThread public android.view.translation.TranslationResponse translate(@NonNull android.view.translation.TranslationRequest);
}
+ public final class UiTranslationManager {
+ method public void registerUiTranslationStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.view.translation.UiTranslationStateCallback);
+ method public void unregisterUiTranslationStateCallback(@NonNull android.view.translation.UiTranslationStateCallback);
+ }
+
+ public interface UiTranslationStateCallback {
+ method public void onFinished();
+ method public void onPaused();
+ method public void onStarted(@NonNull String, @NonNull String);
+ }
+
public final class ViewTranslationRequest implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.view.autofill.AutofillId getAutofillId();
@@ -55024,6 +55050,7 @@
method public void setRelativeScrollPosition(@IdRes int, int);
method @Deprecated public void setRemoteAdapter(int, @IdRes int, android.content.Intent);
method public void setRemoteAdapter(@IdRes int, android.content.Intent);
+ method public void setRemoteAdapter(@IdRes int, @NonNull android.widget.RemoteViews.RemoteCollectionItems);
method public void setScrollPosition(@IdRes int, int);
method public void setShort(@IdRes int, String, short);
method public void setString(@IdRes int, String, String);
@@ -55063,6 +55090,25 @@
ctor public RemoteViews.ActionException(String);
}
+ public static final class RemoteViews.RemoteCollectionItems implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getItemCount();
+ method public long getItemId(int);
+ method @NonNull public android.widget.RemoteViews getItemView(int);
+ method public int getViewTypeCount();
+ method public boolean hasStableIds();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.widget.RemoteViews.RemoteCollectionItems> CREATOR;
+ }
+
+ public static final class RemoteViews.RemoteCollectionItems.Builder {
+ ctor public RemoteViews.RemoteCollectionItems.Builder();
+ method @NonNull public android.widget.RemoteViews.RemoteCollectionItems.Builder addItem(long, @NonNull android.widget.RemoteViews);
+ method @NonNull public android.widget.RemoteViews.RemoteCollectionItems build();
+ method @NonNull public android.widget.RemoteViews.RemoteCollectionItems.Builder setHasStableIds(boolean);
+ method @NonNull public android.widget.RemoteViews.RemoteCollectionItems.Builder setViewTypeCount(int);
+ }
+
public static class RemoteViews.RemoteResponse {
ctor public RemoteViews.RemoteResponse();
method @NonNull public android.widget.RemoteViews.RemoteResponse addSharedElement(@IdRes int, @NonNull String);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 18b0a43..a140e8a 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -55,6 +55,10 @@
package android.content {
+ public abstract class ContentProvider implements android.content.ComponentCallbacks2 {
+ method @NonNull public static android.net.Uri createContentUriAsUser(@NonNull android.net.Uri, @NonNull android.os.UserHandle);
+ }
+
public abstract class Context {
method @NonNull public android.os.UserHandle getUser();
}
@@ -203,6 +207,16 @@
method @Nullable public byte[] getWatchlistConfigHash();
}
+ public class PacProxyManager {
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void addPacProxyInstalledListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.PacProxyManager.PacProxyInstalledListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void removePacProxyInstalledListener(@NonNull android.net.PacProxyManager.PacProxyInstalledListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setCurrentProxyScriptUrl(@Nullable android.net.ProxyInfo);
+ }
+
+ public static interface PacProxyManager.PacProxyInstalledListener {
+ method public void onPacProxyInstalled(@Nullable android.net.Network, @NonNull android.net.ProxyInfo);
+ }
+
public final class Proxy {
method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo);
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index bfc205b..1d1303f 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1795,6 +1795,7 @@
public final class UsageStats implements android.os.Parcelable {
method public int getAppLaunchCount();
+ method public long getLastTimeComponentUsed();
}
public final class UsageStatsManager {
@@ -1888,6 +1889,7 @@
public final class BluetoothDevice implements android.os.Parcelable {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean canBondWithoutDialog();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean cancelBondProcess();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean createBondOutOfBand(int, @Nullable android.bluetooth.OobData, @Nullable android.bluetooth.OobData);
method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public byte[] getMetadata(int);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getSimAccessPermission();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isConnected();
@@ -2064,6 +2066,54 @@
field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BufferConstraints> CREATOR;
}
+ public final class OobData implements android.os.Parcelable {
+ method @NonNull public static android.bluetooth.OobData.ClassicBuilder createClassicBuilder(@NonNull byte[], @NonNull byte[], @NonNull byte[]);
+ method @NonNull public static android.bluetooth.OobData.LeBuilder createLeBuilder(@NonNull byte[], @NonNull byte[], int);
+ method @NonNull public byte[] getClassOfDevice();
+ method @NonNull public byte[] getClassicLength();
+ method @NonNull public byte[] getConfirmationHash();
+ method @NonNull public byte[] getDeviceAddressWithType();
+ method @Nullable public byte[] getDeviceName();
+ method @Nullable public byte[] getLeAppearance();
+ method @NonNull public int getLeDeviceRole();
+ method @NonNull public int getLeFlags();
+ method @Nullable public byte[] getLeTemporaryKey();
+ method @NonNull public byte[] getRandomizerHash();
+ field public static final int CLASS_OF_DEVICE_OCTETS = 3; // 0x3
+ field public static final int CONFIRMATION_OCTETS = 16; // 0x10
+ field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.OobData> CREATOR;
+ field public static final int DEVICE_ADDRESS_OCTETS = 7; // 0x7
+ field public static final int LE_APPEARANCE_OCTETS = 2; // 0x2
+ field public static final int LE_DEVICE_FLAG_OCTETS = 1; // 0x1
+ field public static final int LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL = 3; // 0x3
+ field public static final int LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL = 2; // 0x2
+ field public static final int LE_DEVICE_ROLE_CENTRAL_ONLY = 1; // 0x1
+ field public static final int LE_DEVICE_ROLE_OCTETS = 1; // 0x1
+ field public static final int LE_DEVICE_ROLE_PERIPHERAL_ONLY = 0; // 0x0
+ field public static final int LE_FLAG_BREDR_NOT_SUPPORTED = 2; // 0x2
+ field public static final int LE_FLAG_GENERAL_DISCOVERY_MODE = 1; // 0x1
+ field public static final int LE_FLAG_LIMITED_DISCOVERY_MODE = 0; // 0x0
+ field public static final int LE_FLAG_SIMULTANEOUS_CONTROLLER = 3; // 0x3
+ field public static final int LE_FLAG_SIMULTANEOUS_HOST = 4; // 0x4
+ field public static final int LE_TK_OCTETS = 16; // 0x10
+ field public static final int RANDOMIZER_OCTETS = 16; // 0x10
+ }
+
+ public static final class OobData.ClassicBuilder {
+ method @NonNull public android.bluetooth.OobData build();
+ method @NonNull public android.bluetooth.OobData.ClassicBuilder setClassOfDevice(@NonNull byte[]);
+ method @NonNull public android.bluetooth.OobData.ClassicBuilder setDeviceName(@NonNull byte[]);
+ method @NonNull public android.bluetooth.OobData.ClassicBuilder setRandomizerHash(@NonNull byte[]);
+ }
+
+ public static final class OobData.LeBuilder {
+ method @NonNull public android.bluetooth.OobData build();
+ method @NonNull public android.bluetooth.OobData.LeBuilder setDeviceName(@NonNull byte[]);
+ method @NonNull public android.bluetooth.OobData.LeBuilder setLeFlags(int);
+ method @NonNull public android.bluetooth.OobData.LeBuilder setLeTemporaryKey(@NonNull byte[]);
+ method @NonNull public android.bluetooth.OobData.LeBuilder setRandomizerHash(@NonNull byte[]);
+ }
+
}
package android.bluetooth.le {
@@ -3021,6 +3071,7 @@
field public final float reduceBrightColorsOffset;
field public final int reduceBrightColorsStrength;
field public final long timeStamp;
+ field @NonNull public final String uniqueDisplayId;
}
public final class BrightnessConfiguration implements android.os.Parcelable {
@@ -5145,6 +5196,21 @@
field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int RADIO_TUNER = 1998; // 0x7ce
}
+ public final class MediaRouter2 {
+ method @NonNull public java.util.List<android.media.MediaRoute2Info> getAllRoutes();
+ method @Nullable public String getClientPackageName();
+ method @Nullable public android.media.MediaRouter2.RoutingController getController(@NonNull String);
+ method @Nullable public static android.media.MediaRouter2 getInstance(@NonNull android.content.Context, @NonNull String);
+ method public void setRouteVolume(@NonNull android.media.MediaRoute2Info, int);
+ method public void startScan();
+ method public void stopScan();
+ method public void transfer(@NonNull android.media.MediaRouter2.RoutingController, @NonNull android.media.MediaRoute2Info);
+ }
+
+ public abstract static class MediaRouter2.RouteCallback {
+ method public void onPreferredFeaturesChanged(@NonNull java.util.List<java.lang.String>);
+ }
+
public class PlayerProxy {
method public void pause();
method public void setPan(float);
@@ -9629,10 +9695,28 @@
package android.service.displayhash {
+ public final class DisplayHashParams implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.util.Size getBufferSize();
+ method public boolean isBufferScaleWithFiltering();
+ method public boolean isGrayscaleBuffer();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.displayhash.DisplayHashParams> CREATOR;
+ }
+
+ public static final class DisplayHashParams.Builder {
+ ctor public DisplayHashParams.Builder();
+ method @NonNull public android.service.displayhash.DisplayHashParams build();
+ method @NonNull public android.service.displayhash.DisplayHashParams.Builder setBufferScaleWithFiltering(boolean);
+ method @NonNull public android.service.displayhash.DisplayHashParams.Builder setBufferSize(int, int);
+ method @NonNull public android.service.displayhash.DisplayHashParams.Builder setGrayscaleBuffer(boolean);
+ }
+
public abstract class DisplayHasherService extends android.app.Service {
ctor public DisplayHasherService();
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
method @Nullable public abstract void onGenerateDisplayHash(@NonNull byte[], @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String, @NonNull android.view.displayhash.DisplayHashResultCallback);
+ method @NonNull public abstract java.util.Map<java.lang.String,android.service.displayhash.DisplayHashParams> onGetDisplayHashAlgorithms();
method @Nullable public abstract android.view.displayhash.VerifiedDisplayHash onVerifyDisplayHash(@NonNull byte[], @NonNull android.view.displayhash.DisplayHash);
field public static final String SERVICE_INTERFACE = "android.service.displayhash.DisplayHasherService";
}
@@ -11615,7 +11699,7 @@
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isInEmergencySmsMode();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int);
- method public boolean isNrDualConnectivityEnabled();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isNrDualConnectivityEnabled();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
@@ -11701,6 +11785,7 @@
field public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; // 0x4
field public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; // 0x3
field public static final String CAPABILITY_ALLOWED_NETWORK_TYPES_USED = "CAPABILITY_ALLOWED_NETWORK_TYPES_USED";
+ field public static final String CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE = "CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE";
field public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE = "CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE";
field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 9fde791..d4b3f16 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -28,6 +28,7 @@
field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
field public static final String OVERRIDE_DISPLAY_MODE_REQUESTS = "android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS";
+ field public static final String QUERY_AUDIO_STATE = "android.permission.QUERY_AUDIO_STATE";
field public static final String QUERY_USERS = "android.permission.QUERY_USERS";
field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
@@ -1380,10 +1381,6 @@
method public int getMaxMacroBlocks();
}
- public final class MediaDrm implements java.lang.AutoCloseable {
- method @Nullable public android.media.metrics.PlaybackComponent getPlaybackComponent(@NonNull byte[]);
- }
-
public final class MediaRoute2Info implements android.os.Parcelable {
method @NonNull public String getOriginalId();
}
@@ -1950,8 +1947,7 @@
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);
+ method @RequiresPermission("android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP") @WorkerThread public static boolean setCredentialManagementApp(@NonNull android.content.Context, @NonNull String, @NonNull android.security.AppUriAuthenticationPolicy);
}
public class KeyStoreException extends java.lang.Exception {
@@ -2736,6 +2732,7 @@
public final class InputMethodManager {
method public int getDisplayId();
+ method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int);
method public boolean hasActiveInputConnection(@Nullable android.view.View);
method public boolean isInputMethodPickerShown();
}
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index dab4a5d..d536821 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -555,6 +555,11 @@
*/
public static final int GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS = 14;
+ /**
+ * Action to dismiss the notification shade
+ */
+ public static final int GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE = 15;
+
private static final String LOG_TAG = "AccessibilityService";
/**
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 233f737..a24f871 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -394,6 +394,20 @@
}
/**
+ * Whether to allow non-resizable apps to be shown in multi-window. The app will be letterboxed
+ * if the request orientation is not met, and will be shown in size-compat mode if the container
+ * size has changed.
+ * @hide
+ */
+ public static boolean supportsNonResizableMultiWindow() {
+ try {
+ return ActivityTaskManager.getService().supportsNonResizableMultiWindow();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* @return whether the UI mode of the given config supports error dialogs (ANR, crash, etc).
* @hide
*/
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 3af0763..0358fe5 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2586,9 +2586,7 @@
@Override
public Context createTokenContext(@NonNull IBinder token, @NonNull Display display) {
if (display == null) {
- throw new UnsupportedOperationException("Token context can only be created from "
- + "other visual contexts, such as Activity or one created with "
- + "Context#createDisplayContext(Display)");
+ throw new IllegalArgumentException("Display must not be null");
}
final ContextImpl tokenContext = createBaseWindowContext(token, display);
tokenContext.setResources(createWindowContextResources());
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 542f754..3bfddf7 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -289,6 +289,13 @@
void setSplitScreenResizing(boolean resizing);
boolean supportsLocalVoiceInteraction();
+ /**
+ * Whether to allow non-resizable apps to be shown in multi-window. The app will be letterboxed
+ * if the request orientation is not met, and will be shown in size-compat mode if the container
+ * size has changed.
+ */
+ boolean supportsNonResizableMultiWindow();
+
// Get device configuration
ConfigurationInfo getDeviceConfigurationInfo();
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2b45723..a3a8a5e 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5011,9 +5011,13 @@
boolean showProgress = handleProgressBar(contentView, ex, p);
boolean hasSecondLine = showProgress;
if (p.hasTitle()) {
- contentView.setViewVisibility(R.id.title, View.VISIBLE);
- contentView.setTextViewText(R.id.title, processTextSpans(p.title));
- setTextViewColorPrimary(contentView, R.id.title, p);
+ contentView.setViewVisibility(p.mTitleViewId, View.VISIBLE);
+ contentView.setTextViewText(p.mTitleViewId, processTextSpans(p.title));
+ setTextViewColorPrimary(contentView, p.mTitleViewId, p);
+ } else if (p.mTitleViewId != R.id.title) {
+ // This alternate title view ID is not cleared by resetStandardTemplate
+ contentView.setViewVisibility(p.mTitleViewId, View.GONE);
+ contentView.setTextViewText(p.mTitleViewId, null);
}
if (p.text != null && p.text.length() != 0
&& (!showProgress || p.mAllowTextWithProgress)) {
@@ -5307,7 +5311,7 @@
contentView.setInt(R.id.expand_button, "setDefaultTextColor", textColor);
contentView.setInt(R.id.expand_button, "setDefaultPillColor", pillColor);
// Use different highlighted colors except when low-priority mode prevents that
- if (!p.forceDefaultColor) {
+ if (!p.mReduceHighlights) {
textColor = getBackgroundColor(p);
pillColor = getAccentColor(p);
}
@@ -5441,6 +5445,11 @@
// keep the divider visible between that title and the next text element.
return true;
}
+ if (p.mHideAppName) {
+ // The app name is being hidden, so we definitely want to return here.
+ // Assume that there is a title which will replace it in the header.
+ return p.hasTitle();
+ }
contentView.setViewVisibility(R.id.app_name_text, View.VISIBLE);
contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
contentView.setTextColor(R.id.app_name_text, getSecondaryTextColor(p));
@@ -5817,7 +5826,7 @@
*
* @hide
*/
- public RemoteViews makeNotificationHeader() {
+ public RemoteViews makeNotificationGroupHeader() {
return makeNotificationHeader(mParams.reset()
.viewType(StandardTemplateParams.VIEW_TYPE_GROUP_HEADER)
.fillTextsFrom(this));
@@ -5943,7 +5952,7 @@
.viewType(StandardTemplateParams.VIEW_TYPE_PUBLIC)
.fillTextsFrom(this);
if (isLowPriority) {
- params.forceDefaultColor();
+ params.reduceHighlights();
}
view = makeNotificationHeader(params);
view.setBoolean(R.id.notification_header, "setExpandOnlyOnButton", true);
@@ -5966,7 +5975,7 @@
public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
StandardTemplateParams p = mParams.reset()
.viewType(StandardTemplateParams.VIEW_TYPE_MINIMIZED)
- .forceDefaultColor()
+ .reduceHighlights()
.fillTextsFrom(this);
if (!useRegularSubtext || TextUtils.isEmpty(mParams.summaryText)) {
p.summaryText(createSummaryText());
@@ -6306,7 +6315,9 @@
* @param p the template params to inflate this with
*/
private @ColorInt int getRawColor(StandardTemplateParams p) {
- if (p.forceDefaultColor) {
+ // When notifications are theme-tinted, the raw color is only used for the icon, so go
+ // ahead and keep that color instead of changing the color for minimized notifs.
+ if (p.mReduceHighlights && !mTintWithThemeAccent) {
return COLOR_DEFAULT;
}
return mN.color;
@@ -6583,10 +6594,6 @@
return R.layout.notification_template_material_conversation;
}
- private int getCallLayoutResource() {
- return R.layout.notification_template_material_call;
- }
-
private int getActionLayoutResource() {
return R.layout.notification_material_action;
}
@@ -9329,7 +9336,7 @@
*/
@Override
public RemoteViews makeContentView(boolean increasedHeight) {
- return makeCallLayout();
+ return makeCallLayout(StandardTemplateParams.VIEW_TYPE_NORMAL);
}
/**
@@ -9337,14 +9344,14 @@
*/
@Override
public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
- return makeCallLayout();
+ return makeCallLayout(StandardTemplateParams.VIEW_TYPE_HEADS_UP);
}
/**
* @hide
*/
public RemoteViews makeBigContentView() {
- return makeCallLayout();
+ return makeCallLayout(StandardTemplateParams.VIEW_TYPE_BIG);
}
@NonNull
@@ -9443,8 +9450,10 @@
return resultActions;
}
- private RemoteViews makeCallLayout() {
+ private RemoteViews makeCallLayout(int viewType) {
+ final boolean isCollapsed = viewType == StandardTemplateParams.VIEW_TYPE_NORMAL;
Bundle extras = mBuilder.mN.extras;
+ CharSequence title = mPerson != null ? mPerson.getName() : null;
CharSequence text = mBuilder.processLegacyText(extras.getCharSequence(EXTRA_TEXT));
if (text == null) {
text = getDefaultText();
@@ -9452,20 +9461,30 @@
// Bind standard template
StandardTemplateParams p = mBuilder.mParams.reset()
- .viewType(StandardTemplateParams.VIEW_TYPE_BIG)
+ .viewType(viewType)
.callStyleActions(true)
.allowTextWithProgress(true)
.hideLargeIcon(true)
+ .hideAppName(isCollapsed)
+ .titleViewId(R.id.conversation_text)
+ .title(title)
.text(text)
.summaryText(mBuilder.processLegacyText(mVerificationText));
mBuilder.mActions = getActionsListWithSystemActions();
- RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
- mBuilder.getCallLayoutResource(), p, null /* result */);
+ final RemoteViews contentView;
+ if (isCollapsed) {
+ contentView = mBuilder.applyStandardTemplate(
+ R.layout.notification_template_material_call, p, null /* result */);
+ } else {
+ contentView = mBuilder.applyStandardTemplateWithActions(
+ R.layout.notification_template_material_big_call, p, null /* result */);
+ }
// Bind some extra conversation-specific header fields.
- mBuilder.setTextViewColorPrimary(contentView, R.id.conversation_text, p);
- mBuilder.setTextViewColorSecondary(contentView, R.id.app_name_divider, p);
- contentView.setViewVisibility(R.id.app_name_divider, View.VISIBLE);
+ if (!p.mHideAppName) {
+ mBuilder.setTextViewColorSecondary(contentView, R.id.app_name_divider, p);
+ contentView.setViewVisibility(R.id.app_name_divider, View.VISIBLE);
+ }
bindCallerVerification(contentView, p);
// Bind some custom CallLayout properties
@@ -12142,12 +12161,13 @@
public static int VIEW_TYPE_NORMAL = 1;
public static int VIEW_TYPE_BIG = 2;
public static int VIEW_TYPE_HEADS_UP = 3;
- public static int VIEW_TYPE_MINIMIZED = 4;
- public static int VIEW_TYPE_PUBLIC = 5;
- public static int VIEW_TYPE_GROUP_HEADER = 6;
+ public static int VIEW_TYPE_MINIMIZED = 4; // header only for minimized state
+ public static int VIEW_TYPE_PUBLIC = 5; // header only for automatic public version
+ public static int VIEW_TYPE_GROUP_HEADER = 6; // header only for top of group
int mViewType = VIEW_TYPE_UNSPECIFIED;
boolean mHeaderless;
+ boolean mHideAppName;
boolean mHideTitle;
boolean mHideActions;
boolean mHideProgress;
@@ -12155,6 +12175,7 @@
boolean mPromotePicture;
boolean mCallStyleActions;
boolean mAllowTextWithProgress;
+ int mTitleViewId;
int mTextViewId;
CharSequence title;
CharSequence text;
@@ -12163,11 +12184,12 @@
int maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
boolean hideLargeIcon;
boolean allowColorization = true;
- boolean forceDefaultColor = false;
+ boolean mReduceHighlights = false;
final StandardTemplateParams reset() {
mViewType = VIEW_TYPE_UNSPECIFIED;
mHeaderless = false;
+ mHideAppName = false;
mHideTitle = false;
mHideActions = false;
mHideProgress = false;
@@ -12175,6 +12197,7 @@
mPromotePicture = false;
mCallStyleActions = false;
mAllowTextWithProgress = false;
+ mTitleViewId = R.id.title;
mTextViewId = R.id.text;
title = null;
text = null;
@@ -12182,7 +12205,7 @@
headerTextSecondary = null;
maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
allowColorization = true;
- forceDefaultColor = false;
+ mReduceHighlights = false;
return this;
}
@@ -12200,6 +12223,11 @@
return this;
}
+ public StandardTemplateParams hideAppName(boolean hideAppName) {
+ mHideAppName = hideAppName;
+ return this;
+ }
+
final StandardTemplateParams hideActions(boolean hideActions) {
this.mHideActions = hideActions;
return this;
@@ -12235,6 +12263,11 @@
return this;
}
+ public StandardTemplateParams titleViewId(int titleViewId) {
+ mTitleViewId = titleViewId;
+ return this;
+ }
+
public StandardTemplateParams textViewId(int textViewId) {
mTextViewId = textViewId;
return this;
@@ -12270,8 +12303,8 @@
return this;
}
- final StandardTemplateParams forceDefaultColor() {
- this.forceDefaultColor = true;
+ final StandardTemplateParams reduceHighlights() {
+ this.mReduceHighlights = true;
return this;
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 43c14a9..31b0d41 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -129,11 +129,13 @@
import android.net.IEthernetManager;
import android.net.IIpSecService;
import android.net.INetworkPolicyManager;
+import android.net.IPacProxyManager;
import android.net.IVpnManager;
import android.net.IpSecManager;
import android.net.NetworkPolicyManager;
import android.net.NetworkScoreManager;
import android.net.NetworkWatchlistManager;
+import android.net.PacProxyManager;
import android.net.TetheringManager;
import android.net.VpnManager;
import android.net.lowpan.ILowpanManager;
@@ -374,6 +376,15 @@
// (which extends it).
SYSTEM_SERVICE_NAMES.put(android.text.ClipboardManager.class, Context.CLIPBOARD_SERVICE);
+ registerService(Context.PAC_PROXY_SERVICE, PacProxyManager.class,
+ new CachedServiceFetcher<PacProxyManager>() {
+ @Override
+ public PacProxyManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(Context.PAC_PROXY_SERVICE);
+ IPacProxyManager service = IPacProxyManager.Stub.asInterface(b);
+ return new PacProxyManager(ctx.getOuterContext(), service);
+ }});
+
registerService(Context.NETD_SERVICE, IBinder.class, new StaticServiceFetcher<IBinder>() {
@Override
public IBinder createService() throws ServiceNotFoundException {
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index dcecd90..5d50c5d7 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -20,6 +20,7 @@
import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED;
import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED;
+import static android.app.usage.UsageEvents.Event.APP_COMPONENT_USED;
import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE;
import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN;
import static android.app.usage.UsageEvents.Event.END_OF_DAY;
@@ -109,6 +110,13 @@
public long mTotalTimeForegroundServiceUsed;
/**
+ * Last time this package's component is used, measured in milliseconds since the epoch.
+ * See {@link UsageEvents.Event#APP_COMPONENT_USED}
+ * @hide
+ */
+ public long mLastTimeComponentUsed;
+
+ /**
* {@hide}
*/
@UnsupportedAppUsage
@@ -166,6 +174,7 @@
mEndTimeStamp = stats.mEndTimeStamp;
mLastTimeUsed = stats.mLastTimeUsed;
mLastTimeVisible = stats.mLastTimeVisible;
+ mLastTimeComponentUsed = stats.mLastTimeComponentUsed;
mLastTimeForegroundServiceUsed = stats.mLastTimeForegroundServiceUsed;
mTotalTimeInForeground = stats.mTotalTimeInForeground;
mTotalTimeVisible = stats.mTotalTimeVisible;
@@ -265,6 +274,16 @@
}
/**
+ * Get the last time this package's component was used, measured in milliseconds since the
+ * epoch.
+ * @hide
+ */
+ @SystemApi
+ public long getLastTimeComponentUsed() {
+ return mLastTimeComponentUsed;
+ }
+
+ /**
* Returns the number of times the app was launched as an activity from outside of the app.
* Excludes intra-app activity transitions.
* @hide
@@ -323,6 +342,7 @@
mergeEventMap(mForegroundServices, right.mForegroundServices);
mLastTimeUsed = Math.max(mLastTimeUsed, right.mLastTimeUsed);
mLastTimeVisible = Math.max(mLastTimeVisible, right.mLastTimeVisible);
+ mLastTimeComponentUsed = Math.max(mLastTimeComponentUsed, right.mLastTimeComponentUsed);
mLastTimeForegroundServiceUsed = Math.max(mLastTimeForegroundServiceUsed,
right.mLastTimeForegroundServiceUsed);
}
@@ -598,6 +618,9 @@
mLastTimeVisible = timeStamp;
}
break;
+ case APP_COMPONENT_USED:
+ mLastTimeComponentUsed = timeStamp;
+ break;
default:
break;
}
@@ -620,6 +643,7 @@
dest.writeLong(mEndTimeStamp);
dest.writeLong(mLastTimeUsed);
dest.writeLong(mLastTimeVisible);
+ dest.writeLong(mLastTimeComponentUsed);
dest.writeLong(mLastTimeForegroundServiceUsed);
dest.writeLong(mTotalTimeInForeground);
dest.writeLong(mTotalTimeVisible);
@@ -674,6 +698,7 @@
stats.mEndTimeStamp = in.readLong();
stats.mLastTimeUsed = in.readLong();
stats.mLastTimeVisible = in.readLong();
+ stats.mLastTimeComponentUsed = in.readLong();
stats.mLastTimeForegroundServiceUsed = in.readLong();
stats.mTotalTimeInForeground = in.readLong();
stats.mTotalTimeVisible = in.readLong();
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index ec94faa..07dbdce 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1311,7 +1311,6 @@
* the bonding process completes, and its result.
* <p>Android system services will handle the necessary user interactions
* to confirm and complete the bonding process.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
*
* @param transport The transport to use for the pairing procedure.
* @return false on immediate error, true if bonding will begin
@@ -1319,8 +1318,9 @@
* @hide
*/
@UnsupportedAppUsage
+ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
public boolean createBond(int transport) {
- return createBondOutOfBand(transport, null);
+ return createBondInternal(transport, null, null);
}
/**
@@ -1334,22 +1334,39 @@
* <p>Android system services will handle the necessary user interactions
* to confirm and complete the bonding process.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
+ * <p>There are two possible versions of OOB Data. This data can come in as
+ * P192 or P256. This is a reference to the cryptography used to generate the key.
+ * The caller may pass one or both. If both types of data are passed, then the
+ * P256 data will be preferred, and thus used.
*
* @param transport - Transport to use
- * @param oobData - Out Of Band data
+ * @param remoteP192Data - Out Of Band data (P192) or null
+ * @param remoteP256Data - Out Of Band data (P256) or null
* @return false on immediate error, true if bonding will begin
* @hide
*/
- public boolean createBondOutOfBand(int transport, OobData oobData) {
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean createBondOutOfBand(int transport, @Nullable OobData remoteP192Data,
+ @Nullable OobData remoteP256Data) {
+ if (remoteP192Data == null && remoteP256Data == null) {
+ throw new IllegalArgumentException(
+ "One or both arguments for the OOB data types are required to not be null."
+ + " Please use createBond() instead if you do not have OOB data to pass.");
+ }
+ return createBondInternal(transport, remoteP192Data, remoteP256Data);
+ }
+
+ private boolean createBondInternal(int transport, @Nullable OobData remoteP192Data,
+ @Nullable OobData remoteP256Data) {
final IBluetooth service = sService;
if (service == null) {
Log.w(TAG, "BT not enabled, createBondOutOfBand failed");
return false;
}
try {
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- return service.createBond(this, transport, oobData, adapter.getOpPackageName());
+ return service.createBond(this, transport, remoteP192Data, remoteP256Data,
+ BluetoothAdapter.getDefaultAdapter().getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1380,27 +1397,6 @@
}
/**
- * Set the Out Of Band data for a remote device to be used later
- * in the pairing mechanism. Users can obtain this data through other
- * trusted channels
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
- *
- * @param hash Simple Secure pairing hash
- * @param randomizer The random key obtained using OOB
- * @return false on error; true otherwise
- * @hide
- */
- public boolean setDeviceOutOfBandData(byte[] hash, byte[] randomizer) {
- //TODO(BT)
- /*
- try {
- return sService.setDeviceOutOfBandData(this, hash, randomizer);
- } catch (RemoteException e) {Log.e(TAG, "", e);} */
- return false;
- }
-
- /**
* Cancel an in-progress bonding request started with {@link #createBond}.
*
* @return true on success, false on error
diff --git a/core/java/android/bluetooth/OobData.java b/core/java/android/bluetooth/OobData.java
index 0d0c6ab..9810746 100644
--- a/core/java/android/bluetooth/OobData.java
+++ b/core/java/android/bluetooth/OobData.java
@@ -1,4 +1,4 @@
-/*
+/**
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,88 +16,950 @@
package android.bluetooth;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.util.Preconditions;
+
+import java.lang.IllegalArgumentException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Out Of Band Data for Bluetooth device pairing.
*
* <p>This object represents optional data obtained from a remote device through
- * an out-of-band channel (eg. NFC).
+ * an out-of-band channel (eg. NFC, QR).
+ *
+ * <p>References:
+ * NFC AD Forum SSP 1.1 (AD)
+ * {@link https://members.nfc-forum.org//apps/group_public/download.php/24620/NFCForum-AD-BTSSP_1_1.pdf}
+ * Core Specification Supplement (CSS) V9
+ *
+ * <p>There are several BR/EDR Examples
+ *
+ * <p>Negotiated Handover:
+ * Bluetooth Carrier Configuration Record:
+ * - OOB Data Length
+ * - Device Address
+ * - Class of Device
+ * - Simple Pairing Hash C
+ * - Simple Pairing Randomizer R
+ * - Service Class UUID
+ * - Bluetooth Local Name
+ *
+ * <p>Static Handover:
+ * Bluetooth Carrier Configuration Record:
+ * - OOB Data Length
+ * - Device Address
+ * - Class of Device
+ * - Service Class UUID
+ * - Bluetooth Local Name
+ *
+ * <p>Simplified Tag Format for Single BT Carrier:
+ * Bluetooth OOB Data Record:
+ * - OOB Data Length
+ * - Device Address
+ * - Class of Device
+ * - Service Class UUID
+ * - Bluetooth Local Name
*
* @hide
*/
-public class OobData implements Parcelable {
- private byte[] mLeBluetoothDeviceAddress;
- private byte[] mSecurityManagerTk;
- private byte[] mLeSecureConnectionsConfirmation;
- private byte[] mLeSecureConnectionsRandom;
+@SystemApi
+public final class OobData implements Parcelable {
- public byte[] getLeBluetoothDeviceAddress() {
- return mLeBluetoothDeviceAddress;
+ private static final String TAG = "OobData";
+ /** The {@link OobData#mClassicLength} may be. (AD 3.1.1) (CSS 1.6.2) @hide */
+ @SystemApi
+ private static final int OOB_LENGTH_OCTETS = 2;
+ /**
+ * The length for the {@link OobData#mDeviceAddressWithType}(6) and Address Type(1).
+ * (AD 3.1.2) (CSS 1.6.2)
+ * @hide
+ */
+ @SystemApi
+ public static final int DEVICE_ADDRESS_OCTETS = 7;
+ /** The Class of Device is 3 octets. (AD 3.1.3) (CSS 1.6.2) @hide */
+ @SystemApi
+ public static final int CLASS_OF_DEVICE_OCTETS = 3;
+ /** The Confirmation data must be 16 octets. (AD 3.2.2) (CSS 1.6.2) @hide */
+ @SystemApi
+ public static final int CONFIRMATION_OCTETS = 16;
+ /** The Randomizer data must be 16 octets. (AD 3.2.3) (CSS 1.6.2) @hide */
+ @SystemApi
+ public static final int RANDOMIZER_OCTETS = 16;
+ /** The LE Device Role length is 1 octet. (AD 3.3.2) (CSS 1.17) @hide */
+ @SystemApi
+ public static final int LE_DEVICE_ROLE_OCTETS = 1;
+ /** The {@link OobData#mLeTemporaryKey} length. (3.4.1) @hide */
+ @SystemApi
+ public static final int LE_TK_OCTETS = 16;
+ /** The {@link OobData#mLeAppearance} length. (3.4.1) @hide */
+ @SystemApi
+ public static final int LE_APPEARANCE_OCTETS = 2;
+ /** The {@link OobData#mLeFlags} length. (3.4.1) @hide */
+ @SystemApi
+ public static final int LE_DEVICE_FLAG_OCTETS = 1; // 1 octet to hold the 0-4 value.
+
+ // Le Roles
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = { "LE_DEVICE_ROLE_" },
+ value = {
+ LE_DEVICE_ROLE_PERIPHERAL_ONLY,
+ LE_DEVICE_ROLE_CENTRAL_ONLY,
+ LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL,
+ LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL
+ }
+ )
+ public @interface LeRole {}
+
+ /** @hide */
+ @SystemApi
+ public static final int LE_DEVICE_ROLE_PERIPHERAL_ONLY = 0x00;
+ /** @hide */
+ @SystemApi
+ public static final int LE_DEVICE_ROLE_CENTRAL_ONLY = 0x01;
+ /** @hide */
+ @SystemApi
+ public static final int LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL = 0x02;
+ /** @hide */
+ @SystemApi
+ public static final int LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL = 0x03;
+
+ // Le Flags
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = { "LE_FLAG_" },
+ value = {
+ LE_FLAG_LIMITED_DISCOVERY_MODE,
+ LE_FLAG_GENERAL_DISCOVERY_MODE,
+ LE_FLAG_BREDR_NOT_SUPPORTED,
+ LE_FLAG_SIMULTANEOUS_CONTROLLER,
+ LE_FLAG_SIMULTANEOUS_HOST
+ }
+ )
+ public @interface LeFlag {}
+
+ /** @hide */
+ @SystemApi
+ public static final int LE_FLAG_LIMITED_DISCOVERY_MODE = 0x00;
+ /** @hide */
+ @SystemApi
+ public static final int LE_FLAG_GENERAL_DISCOVERY_MODE = 0x01;
+ /** @hide */
+ @SystemApi
+ public static final int LE_FLAG_BREDR_NOT_SUPPORTED = 0x02;
+ /** @hide */
+ @SystemApi
+ public static final int LE_FLAG_SIMULTANEOUS_CONTROLLER = 0x03;
+ /** @hide */
+ @SystemApi
+ public static final int LE_FLAG_SIMULTANEOUS_HOST = 0x04;
+
+ /**
+ * Main creation method for creating a Classic version of {@link OobData}.
+ *
+ * <p>This object will allow the caller to call {@link ClassicBuilder#build()}
+ * to build the data object or add any option information to the builder.
+ *
+ * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS} octets
+ * of data. Data is derived from controller/host stack and is required for pairing OOB.
+ * @param classicLength byte array representing the length of data from 8-65535 across 2
+ * octets (0xXXXX).
+ * @param deviceAddressWithType byte array representing the Bluetooth Address of the device
+ * that owns the OOB data. (i.e. the originator) [6 octets]
+ *
+ * @return a Classic Builder instance with all the given data set or null.
+ *
+ * @throws IllegalArgumentException if any of the values fail to be set.
+ * @throws NullPointerException if any argument is null.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public static ClassicBuilder createClassicBuilder(@NonNull byte[] confirmationHash,
+ @NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType) {
+ return new ClassicBuilder(confirmationHash, classicLength, deviceAddressWithType);
}
/**
- * Sets the LE Bluetooth Device Address value to be used during LE pairing.
- * The value shall be 7 bytes. Please see Bluetooth CSSv6, Part A 1.16 for
- * a detailed description.
+ * Main creation method for creating a LE version of {@link OobData}.
+ *
+ * <p>This object will allow the caller to call {@link LeBuilder#build()}
+ * to build the data object or add any option information to the builder.
+ *
+ * @param deviceAddressWithType the LE device address plus the address type (7 octets);
+ * not null.
+ * @param leDeviceRole whether the device supports Peripheral, Central,
+ * Both including preference; not null. (1 octet)
+ * @param confirmationHash Array consisting of {@link OobData#CONFIRMATION_OCTETS} octets
+ * of data. Data is derived from controller/host stack and is
+ * required for pairing OOB.
+ *
+ * <p>Possible LE Device Role Values:
+ * 0x00 Only Peripheral supported
+ * 0x01 Only Central supported
+ * 0x02 Central & Peripheral supported; Peripheral Preferred
+ * 0x03 Only peripheral supported; Central Preferred
+ * 0x04 - 0xFF Reserved
+ *
+ * @return a LeBuilder instance with all the given data set or null.
+ *
+ * @throws IllegalArgumentException if any of the values fail to be set.
+ * @throws NullPointerException if any argument is null.
+ *
+ * @hide
*/
- public void setLeBluetoothDeviceAddress(byte[] leBluetoothDeviceAddress) {
- mLeBluetoothDeviceAddress = leBluetoothDeviceAddress;
- }
-
- public byte[] getSecurityManagerTk() {
- return mSecurityManagerTk;
+ @NonNull
+ @SystemApi
+ public static LeBuilder createLeBuilder(@NonNull byte[] confirmationHash,
+ @NonNull byte[] deviceAddressWithType, @LeRole int leDeviceRole) {
+ return new LeBuilder(confirmationHash, deviceAddressWithType, leDeviceRole);
}
/**
- * Sets the Temporary Key value to be used by the LE Security Manager during
- * LE pairing. The value shall be 16 bytes. Please see Bluetooth CSSv6,
- * Part A 1.8 for a detailed description.
+ * Builds an {@link OobData} object and validates that the required combination
+ * of values are present to create the LE specific OobData type.
+ *
+ * @hide
*/
- public void setSecurityManagerTk(byte[] securityManagerTk) {
- mSecurityManagerTk = securityManagerTk;
+ @SystemApi
+ public static final class LeBuilder {
+
+ /**
+ * It is recommended that this Hash C is generated anew for each
+ * pairing.
+ *
+ * <p>It should be noted that on passive NFC this isn't possible as the data is static
+ * and immutable.
+ */
+ private byte[] mConfirmationHash = null;
+
+ /**
+ * Optional, but adds more validity to the pairing.
+ *
+ * <p>If not present a value of 0 is assumed.
+ */
+ private byte[] mRandomizerHash = new byte[] {
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ };
+
+ /**
+ * The Bluetooth Device user-friendly name presented over Bluetooth Technology.
+ *
+ * <p>This is the name that may be displayed to the device user as part of the UI.
+ */
+ private byte[] mDeviceName = null;
+
+ /**
+ * Sets the Bluetooth Device name to be used for UI purposes.
+ *
+ * <p>Optional attribute.
+ *
+ * @param deviceName byte array representing the name, may be 0 in length, not null.
+ *
+ * @return {@link OobData#ClassicBuilder}
+ *
+ * @throws NullPointerException if deviceName is null.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public LeBuilder setDeviceName(@NonNull byte[] deviceName) {
+ Preconditions.checkNotNull(deviceName);
+ this.mDeviceName = deviceName;
+ return this;
+ }
+
+ /**
+ * The Bluetooth Device Address is the address to which the OOB data belongs.
+ *
+ * <p>The length MUST be {@link OobData#DEVICE_ADDRESS_OCTETS} octets.
+ *
+ * <p> Address is encoded in Little Endian order.
+ *
+ * <p>e.g. 00:01:02:03:04:05 would be x05x04x03x02x01x00
+ */
+ private final byte[] mDeviceAddressWithType;
+
+ /**
+ * During an LE connection establishment, one must be in the Peripheral mode and the other
+ * in the Central role.
+ *
+ * <p>Possible Values:
+ * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported
+ * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported
+ * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported;
+ * Peripheral Preferred
+ * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred
+ * 0x04 - 0xFF Reserved
+ */
+ private final @LeRole int mLeDeviceRole;
+
+ /**
+ * Temporary key value from the Security Manager.
+ *
+ * <p> Must be {@link LE_TK_OCTETS} in size
+ */
+ private byte[] mLeTemporaryKey = null;
+
+ /**
+ * Defines the representation of the external appearance of the device.
+ *
+ * <p>For example, a mouse, remote control, or keyboard.
+ *
+ * <p>Used for visual on discovering device to represent icon/string/etc...
+ */
+ private byte[] mLeAppearance = null;
+
+ /**
+ * Contains which discoverable mode to use, BR/EDR support and capability.
+ *
+ * <p>Possible LE Flags:
+ * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode.
+ * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode.
+ * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of
+ * LMP Feature Mask Definitions.
+ * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to
+ * Same Device Capable (Controller).
+ * Bit 49 of LMP Feature Mask Definitions.
+ * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to
+ * Same Device Capable (Host).
+ * Bit 55 of LMP Feature Mask Definitions.
+ * <b>0x05- 0x07 Reserved</b>
+ */
+ private @LeFlag int mLeFlags = LE_FLAG_GENERAL_DISCOVERY_MODE; // Invalid default
+
+ /**
+ * Constructing an OobData object for use with LE requires
+ * a LE Device Address and LE Device Role as well as the Confirmation
+ * and optionally, the Randomizer, however it is recommended to use.
+ *
+ * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS}
+ * octets of data. Data is derived from controller/host stack and is required for
+ * pairing OOB.
+ * @param deviceAddressWithType 7 bytes containing the 6 byte address with the 1 byte
+ * address type.
+ * @param leDeviceRole indicating device's role and preferences (Central or Peripheral)
+ *
+ * <p>Possible Values:
+ * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported
+ * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported
+ * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported;
+ * Peripheral Preferred
+ * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred
+ * 0x04 - 0xFF Reserved
+ *
+ * @throws IllegalArgumentException if deviceAddressWithType is not
+ * {@link LE_DEVICE_ADDRESS_OCTETS} octets
+ * @throws NullPointerException if any argument is null.
+ */
+ private LeBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] deviceAddressWithType,
+ @LeRole int leDeviceRole) {
+ Preconditions.checkNotNull(confirmationHash);
+ Preconditions.checkNotNull(deviceAddressWithType);
+ if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) {
+ throw new IllegalArgumentException("confirmationHash must be "
+ + OobData.CONFIRMATION_OCTETS + " octets in length.");
+ }
+ this.mConfirmationHash = confirmationHash;
+ if (deviceAddressWithType.length != OobData.DEVICE_ADDRESS_OCTETS) {
+ throw new IllegalArgumentException("confirmationHash must be "
+ + OobData.DEVICE_ADDRESS_OCTETS+ " octets in length.");
+ }
+ this.mDeviceAddressWithType = deviceAddressWithType;
+ if (leDeviceRole < LE_DEVICE_ROLE_PERIPHERAL_ONLY
+ || leDeviceRole > LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL) {
+ throw new IllegalArgumentException("leDeviceRole must be a valid value.");
+ }
+ this.mLeDeviceRole = leDeviceRole;
+ }
+
+ /**
+ * Sets the Temporary Key value to be used by the LE Security Manager during
+ * LE pairing.
+ *
+ * @param leTemporaryKey byte array that shall be 16 bytes. Please see Bluetooth CSSv6,
+ * Part A 1.8 for a detailed description.
+ *
+ * @return {@link OobData#Builder}
+ *
+ * @throws IllegalArgumentException if the leTemporaryKey is an invalid format.
+ * @throws NullinterException if leTemporaryKey is null.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public LeBuilder setLeTemporaryKey(@NonNull byte[] leTemporaryKey) {
+ Preconditions.checkNotNull(leTemporaryKey);
+ if (leTemporaryKey.length != LE_TK_OCTETS) {
+ throw new IllegalArgumentException("leTemporaryKey must be "
+ + LE_TK_OCTETS + " octets in length.");
+ }
+ this.mLeTemporaryKey = leTemporaryKey;
+ return this;
+ }
+
+ /**
+ * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets
+ * of data. Data is derived from controller/host stack and is required for pairing OOB.
+ * Also, randomizerHash may be all 0s or null in which case it becomes all 0s.
+ *
+ * @throws IllegalArgumentException if null or incorrect length randomizerHash was passed.
+ * @throws NullPointerException if randomizerHash is null.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public LeBuilder setRandomizerHash(@NonNull byte[] randomizerHash) {
+ Preconditions.checkNotNull(randomizerHash);
+ if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) {
+ throw new IllegalArgumentException("randomizerHash must be "
+ + OobData.RANDOMIZER_OCTETS + " octets in length.");
+ }
+ this.mRandomizerHash = randomizerHash;
+ return this;
+ }
+
+ /**
+ * Sets the LE Flags necessary for the pairing scenario or discovery mode.
+ *
+ * @param leFlags enum value representing the 1 octet of data about discovery modes.
+ *
+ * <p>Possible LE Flags:
+ * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode.
+ * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode.
+ * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of
+ * LMP Feature Mask Definitions.
+ * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to
+ * Same Device Capable (Controller) Bit 49 of LMP Feature Mask Definitions.
+ * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to
+ * Same Device Capable (Host).
+ * Bit 55 of LMP Feature Mask Definitions.
+ * 0x05- 0x07 Reserved
+ *
+ * @throws IllegalArgumentException for invalid flag
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public LeBuilder setLeFlags(@LeFlag int leFlags) {
+ if (leFlags < LE_FLAG_LIMITED_DISCOVERY_MODE || leFlags > LE_FLAG_SIMULTANEOUS_HOST) {
+ throw new IllegalArgumentException("leFlags must be a valid value.");
+ }
+ this.mLeFlags = leFlags;
+ return this;
+ }
+
+ /**
+ * Validates and builds the {@link OobData} object for LE Security.
+ *
+ * @return {@link OobData} with given builder values
+ *
+ * @throws IllegalStateException if either of the 2 required fields were not set.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public OobData build() {
+ final OobData oob =
+ new OobData(this.mDeviceAddressWithType, this.mLeDeviceRole,
+ this.mConfirmationHash);
+
+ // If we have values, set them, otherwise use default
+ oob.mLeTemporaryKey =
+ (this.mLeTemporaryKey != null) ? this.mLeTemporaryKey : oob.mLeTemporaryKey;
+ oob.mLeAppearance = (this.mLeAppearance != null)
+ ? this.mLeAppearance : oob.mLeAppearance;
+ oob.mLeFlags = (this.mLeFlags != 0xF) ? this.mLeFlags : oob.mLeFlags;
+ oob.mDeviceName = (this.mDeviceName != null) ? this.mDeviceName : oob.mDeviceName;
+ oob.mRandomizerHash = this.mRandomizerHash;
+ return oob;
+ }
}
- public byte[] getLeSecureConnectionsConfirmation() {
- return mLeSecureConnectionsConfirmation;
+ /**
+ * Builds an {@link OobData} object and validates that the required combination
+ * of values are present to create the Classic specific OobData type.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class ClassicBuilder {
+ // Used by both Classic and LE
+ /**
+ * It is recommended that this Hash C is generated anew for each
+ * pairing.
+ *
+ * <p>It should be noted that on passive NFC this isn't possible as the data is static
+ * and immutable.
+ *
+ * @hide
+ */
+ private byte[] mConfirmationHash = null;
+
+ /**
+ * Optional, but adds more validity to the pairing.
+ *
+ * <p>If not present a value of 0 is assumed.
+ *
+ * @hide
+ */
+ private byte[] mRandomizerHash = new byte[] {
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ };
+
+ /**
+ * The Bluetooth Device user-friendly name presented over Bluetooth Technology.
+ *
+ * <p>This is the name that may be displayed to the device user as part of the UI.
+ *
+ * @hide
+ */
+ private byte[] mDeviceName = null;
+
+ /**
+ * This length value provides the absolute length of total OOB data block used for
+ * Bluetooth BR/EDR
+ *
+ * <p>OOB communication, which includes the length field itself and the Bluetooth
+ * Device Address.
+ *
+ * <p>The minimum length that may be represented in this field is 8.
+ *
+ * @hide
+ */
+ private final byte[] mClassicLength;
+
+ /**
+ * The Bluetooth Device Address is the address to which the OOB data belongs.
+ *
+ * <p>The length MUST be {@link OobData#DEVICE_ADDRESS_OCTETS} octets.
+ *
+ * <p> Address is encoded in Little Endian order.
+ *
+ * <p>e.g. 00:01:02:03:04:05 would be x05x04x03x02x01x00
+ *
+ * @hide
+ */
+ private final byte[] mDeviceAddressWithType;
+
+ /**
+ * Class of Device information is to be used to provide a graphical representation
+ * to the user as part of UI involving operations.
+ *
+ * <p>This is not to be used to determine a particular service can be used.
+ *
+ * <p>The length MUST be {@link OobData#CLASS_OF_DEVICE_OCTETS} octets.
+ *
+ * @hide
+ */
+ private byte[] mClassOfDevice = null;
+
+ /**
+ * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS}
+ * octets of data. Data is derived from controller/host stack and is required for pairing
+ * OOB.
+ * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets
+ * of data. Data is derived from controller/host stack and is required
+ * for pairing OOB. Also, randomizerHash may be all 0s or null in which case
+ * it becomes all 0s.
+ * @param classicLength byte array representing the length of data from 8-65535 across 2
+ * octets (0xXXXX). Inclusive of this value in the length.
+ * @param deviceAddressWithType byte array representing the Bluetooth Address of the device
+ * that owns the OOB data. (i.e. the originator) [7 octets] this includes the Address Type
+ * as the last octet.
+ *
+ * @throws IllegalArgumentException if any value is not the correct length
+ * @throws NullPointerException if anything passed is null
+ *
+ * @hide
+ */
+ @SystemApi
+ private ClassicBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] classicLength,
+ @NonNull byte[] deviceAddressWithType) {
+ Preconditions.checkNotNull(confirmationHash);
+ Preconditions.checkNotNull(classicLength);
+ Preconditions.checkNotNull(deviceAddressWithType);
+ if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) {
+ throw new IllegalArgumentException("confirmationHash must be "
+ + OobData.CONFIRMATION_OCTETS + " octets in length.");
+ }
+ this.mConfirmationHash = confirmationHash;
+ if (classicLength.length != OOB_LENGTH_OCTETS) {
+ throw new IllegalArgumentException("classicLength must be "
+ + OOB_LENGTH_OCTETS + " octets in length.");
+ }
+ this.mClassicLength = classicLength;
+ if (deviceAddressWithType.length != DEVICE_ADDRESS_OCTETS) {
+ throw new IllegalArgumentException("deviceAddressWithType must be "
+ + DEVICE_ADDRESS_OCTETS + " octets in length.");
+ }
+ this.mDeviceAddressWithType = deviceAddressWithType;
+ }
+
+ /**
+ * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets
+ * of data. Data is derived from controller/host stack and is required for pairing OOB.
+ * Also, randomizerHash may be all 0s or null in which case it becomes all 0s.
+ *
+ * @throws IllegalArgumentException if null or incorrect length randomizerHash was passed.
+ * @throws NullPointerException if randomizerHash is null.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public ClassicBuilder setRandomizerHash(@NonNull byte[] randomizerHash) {
+ Preconditions.checkNotNull(randomizerHash);
+ if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) {
+ throw new IllegalArgumentException("randomizerHash must be "
+ + OobData.RANDOMIZER_OCTETS + " octets in length.");
+ }
+ this.mRandomizerHash = randomizerHash;
+ return this;
+ }
+
+ /**
+ * Sets the Bluetooth Device name to be used for UI purposes.
+ *
+ * <p>Optional attribute.
+ *
+ * @param deviceName byte array representing the name, may be 0 in length, not null.
+ *
+ * @return {@link OobData#ClassicBuilder}
+ *
+ * @throws NullPointerException if deviceName is null
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public ClassicBuilder setDeviceName(@NonNull byte[] deviceName) {
+ Preconditions.checkNotNull(deviceName);
+ this.mDeviceName = deviceName;
+ return this;
+ }
+
+ /**
+ * Sets the Bluetooth Class of Device; used for UI purposes only.
+ *
+ * <p>Not an indicator of available services!
+ *
+ * <p>Optional attribute.
+ *
+ * @param classOfDevice byte array of {@link OobData#CLASS_OF_DEVICE_OCTETS} octets.
+ *
+ * @return {@link OobData#ClassicBuilder}
+ *
+ * @throws IllegalArgumentException if length is not equal to
+ * {@link OobData#CLASS_OF_DEVICE_OCTETS} octets.
+ * @throws NullPointerException if classOfDevice is null.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public ClassicBuilder setClassOfDevice(@NonNull byte[] classOfDevice) {
+ Preconditions.checkNotNull(classOfDevice);
+ if (classOfDevice.length != OobData.CLASS_OF_DEVICE_OCTETS) {
+ throw new IllegalArgumentException("classOfDevice must be "
+ + OobData.CLASS_OF_DEVICE_OCTETS + " octets in length.");
+ }
+ this.mClassOfDevice = classOfDevice;
+ return this;
+ }
+
+ /**
+ * Validates and builds the {@link OobDat object for Classic Security.
+ *
+ * @return {@link OobData} with previously given builder values.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public OobData build() {
+ final OobData oob =
+ new OobData(this.mClassicLength, this.mDeviceAddressWithType,
+ this.mConfirmationHash);
+ // If we have values, set them, otherwise use default
+ oob.mDeviceName = (this.mDeviceName != null) ? this.mDeviceName : oob.mDeviceName;
+ oob.mClassOfDevice = (this.mClassOfDevice != null)
+ ? this.mClassOfDevice : oob.mClassOfDevice;
+ oob.mRandomizerHash = this.mRandomizerHash;
+ return oob;
+ }
}
- public void setLeSecureConnectionsConfirmation(byte[] leSecureConnectionsConfirmation) {
- mLeSecureConnectionsConfirmation = leSecureConnectionsConfirmation;
+ // Members (Defaults for Optionals must be set or Parceling fails on NPE)
+ // Both
+ private final byte[] mDeviceAddressWithType;
+ private final byte[] mConfirmationHash;
+ private byte[] mRandomizerHash = new byte[] {
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ };
+ // Default the name to "Bluetooth Device"
+ private byte[] mDeviceName = new byte[] {
+ // Bluetooth
+ 0x42, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68,
+ // <space>Device
+ 0x20, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65
+ };
+
+ // Classic
+ private final byte[] mClassicLength;
+ private byte[] mClassOfDevice = new byte[CLASS_OF_DEVICE_OCTETS];
+
+ // LE
+ private final @LeRole int mLeDeviceRole;
+ private byte[] mLeTemporaryKey = new byte[LE_TK_OCTETS];
+ private byte[] mLeAppearance = new byte[LE_APPEARANCE_OCTETS];
+ private @LeFlag int mLeFlags = LE_FLAG_LIMITED_DISCOVERY_MODE;
+
+ /**
+ * @return byte array representing the MAC address of a bluetooth device.
+ * The Address is 6 octets long with a 1 octet address type associated with the address.
+ *
+ * <p>For classic this will be 6 byte address plus the default of PUBLIC_ADDRESS Address Type.
+ * For LE there are more choices for Address Type.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public byte[] getDeviceAddressWithType() {
+ return mDeviceAddressWithType;
}
- public byte[] getLeSecureConnectionsRandom() {
- return mLeSecureConnectionsRandom;
+ /**
+ * @return byte array representing the confirmationHash value
+ * which is used to confirm the identity to the controller.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public byte[] getConfirmationHash() {
+ return mConfirmationHash;
}
- public void setLeSecureConnectionsRandom(byte[] leSecureConnectionsRandom) {
- mLeSecureConnectionsRandom = leSecureConnectionsRandom;
+ /**
+ * @return byte array representing the randomizerHash value
+ * which is used to verify the identity of the controller.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public byte[] getRandomizerHash() {
+ return mRandomizerHash;
}
- public OobData() {
+ /**
+ * @return Device Name used for displaying name in UI.
+ *
+ * <p>Also, this will be populated with the LE Local Name if the data is for LE.
+ *
+ * @hide
+ */
+ @Nullable
+ @SystemApi
+ public byte[] getDeviceName() {
+ return mDeviceName;
+ }
+
+ /**
+ * @return byte array representing the oob data length which is the length
+ * of all of the data including these octets.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public byte[] getClassicLength() {
+ return mClassicLength;
+ }
+
+ /**
+ * @return byte array representing the class of device for UI display.
+ *
+ * <p>Does not indicate services available; for display only.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public byte[] getClassOfDevice() {
+ return mClassOfDevice;
+ }
+
+ /**
+ * @return Temporary Key used for LE pairing.
+ *
+ * @hide
+ */
+ @Nullable
+ @SystemApi
+ public byte[] getLeTemporaryKey() {
+ return mLeTemporaryKey;
+ }
+
+ /**
+ * @return Appearance used for LE pairing. For use in UI situations
+ * when determining what sort of icons or text to display regarding
+ * the device.
+ *
+ * @hide
+ */
+ @Nullable
+ @SystemApi
+ public byte[] getLeAppearance() {
+ return mLeTemporaryKey;
+ }
+
+ /**
+ * @return Flags used to determing discoverable mode to use, BR/EDR Support, and Capability.
+ *
+ * <p>Possible LE Flags:
+ * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode.
+ * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode.
+ * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of
+ * LMP Feature Mask Definitions.
+ * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to
+ * Same Device Capable (Controller).
+ * Bit 49 of LMP Feature Mask Definitions.
+ * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to
+ * Same Device Capable (Host).
+ * Bit 55 of LMP Feature Mask Definitions.
+ * <b>0x05- 0x07 Reserved</b>
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ @LeFlag
+ public int getLeFlags() {
+ return mLeFlags;
+ }
+
+ /**
+ * @return the supported and preferred roles of the LE device.
+ *
+ * <p>Possible Values:
+ * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported
+ * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported
+ * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported;
+ * Peripheral Preferred
+ * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred
+ * 0x04 - 0xFF Reserved
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ @LeRole
+ public int getLeDeviceRole() {
+ return mLeDeviceRole;
+ }
+
+ /**
+ * Classic Security Constructor
+ */
+ private OobData(@NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType,
+ @NonNull byte[] confirmationHash) {
+ mClassicLength = classicLength;
+ mDeviceAddressWithType = deviceAddressWithType;
+ mConfirmationHash = confirmationHash;
+ mLeDeviceRole = -1; // Satisfy final
+ }
+
+ /**
+ * LE Security Constructor
+ */
+ private OobData(@NonNull byte[] deviceAddressWithType, @LeRole int leDeviceRole,
+ @NonNull byte[] confirmationHash) {
+ mDeviceAddressWithType = deviceAddressWithType;
+ mLeDeviceRole = leDeviceRole;
+ mConfirmationHash = confirmationHash;
+ mClassicLength = new byte[OOB_LENGTH_OCTETS]; // Satisfy final
}
private OobData(Parcel in) {
- mLeBluetoothDeviceAddress = in.createByteArray();
- mSecurityManagerTk = in.createByteArray();
- mLeSecureConnectionsConfirmation = in.createByteArray();
- mLeSecureConnectionsRandom = in.createByteArray();
+ // Both
+ mDeviceAddressWithType = in.createByteArray();
+ mConfirmationHash = in.createByteArray();
+ mRandomizerHash = in.createByteArray();
+ mDeviceName = in.createByteArray();
+
+ // Classic
+ mClassicLength = in.createByteArray();
+ mClassOfDevice = in.createByteArray();
+
+ // LE
+ mLeDeviceRole = in.readInt();
+ mLeTemporaryKey = in.createByteArray();
+ mLeAppearance = in.createByteArray();
+ mLeFlags = in.readInt();
}
+ /**
+ * @hide
+ */
@Override
public int describeContents() {
return 0;
}
+ /**
+ * @hide
+ */
@Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeByteArray(mLeBluetoothDeviceAddress);
- out.writeByteArray(mSecurityManagerTk);
- out.writeByteArray(mLeSecureConnectionsConfirmation);
- out.writeByteArray(mLeSecureConnectionsRandom);
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ // Both
+ // Required
+ out.writeByteArray(mDeviceAddressWithType);
+ // Required
+ out.writeByteArray(mConfirmationHash);
+ // Optional
+ out.writeByteArray(mRandomizerHash);
+ // Optional
+ out.writeByteArray(mDeviceName);
+
+ // Classic
+ // Required
+ out.writeByteArray(mClassicLength);
+ // Optional
+ out.writeByteArray(mClassOfDevice);
+
+ // LE
+ // Required
+ out.writeInt(mLeDeviceRole);
+ // Required
+ out.writeByteArray(mLeTemporaryKey);
+ // Optional
+ out.writeByteArray(mLeAppearance);
+ // Optional
+ out.writeInt(mLeFlags);
}
+ // For Parcelable
public static final @android.annotation.NonNull Parcelable.Creator<OobData> CREATOR =
new Parcelable.Creator<OobData>() {
public OobData createFromParcel(Parcel in) {
@@ -108,4 +970,47 @@
return new OobData[size];
}
};
+
+ /**
+ * @return a {@link String} representation of the OobData object.
+ *
+ * @hide
+ */
+ @Override
+ @NonNull
+ public String toString() {
+ return "OobData: \n\t"
+ // Both
+ + "Device Address With Type: " + toHexString(mDeviceAddressWithType) + "\n\t"
+ + "Confirmation: " + toHexString(mConfirmationHash) + "\n\t"
+ + "Randomizer: " + toHexString(mRandomizerHash) + "\n\t"
+ + "Device Name: " + toHexString(mDeviceName) + "\n\t"
+ // Classic
+ + "OobData Length: " + toHexString(mClassicLength) + "\n\t"
+ + "Class of Device: " + toHexString(mClassOfDevice) + "\n\t"
+ // LE
+ + "LE Device Role: " + toHexString(mLeDeviceRole) + "\n\t"
+ + "LE Temporary Key: " + toHexString(mLeTemporaryKey) + "\n\t"
+ + "LE Appearance: " + toHexString(mLeAppearance) + "\n\t"
+ + "LE Flags: " + toHexString(mLeFlags) + "\n\t";
+ }
+
+ @NonNull
+ private String toHexString(@NonNull int b) {
+ return toHexString(new byte[] {(byte) b});
+ }
+
+ @NonNull
+ private String toHexString(@NonNull byte b) {
+ return toHexString(new byte[] {b});
+ }
+
+ @NonNull
+ private String toHexString(@NonNull byte[] array) {
+ StringBuilder builder = new StringBuilder(array.length * 2);
+ for (byte b: array) {
+ builder.append(String.format("%02x", b));
+ }
+ return builder.toString();
+ }
}
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index f3ecbf6..6ab1975 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -21,6 +21,7 @@
import static android.content.ContentResolver.SCHEME_CONTENT;
import static android.content.ContentResolver.SCHEME_FILE;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.res.AssetFileDescriptor;
@@ -38,6 +39,7 @@
import android.text.style.URLSpan;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
+import android.view.textclassifier.TextLinks;
import com.android.internal.util.ArrayUtils;
@@ -204,6 +206,7 @@
Uri mUri;
// Additional activity info resolved by the system
ActivityInfo mActivityInfo;
+ private TextLinks mTextLinks;
/** @hide */
public Item(Item other) {
@@ -332,6 +335,29 @@
}
/**
+ * Returns the results of text classification run on the raw text contained in this item,
+ * if it was performed, and if any entities were found in the text. Classification is
+ * generally only performed on the first item in clip data, and only if the text is below a
+ * certain length.
+ *
+ * <p>Returns {@code null} if classification was not performed, or if no entities were
+ * found in the text.
+ *
+ * @see ClipDescription#getConfidenceScore(String)
+ */
+ @Nullable
+ public TextLinks getTextLinks() {
+ return mTextLinks;
+ }
+
+ /**
+ * @hide
+ */
+ public void setTextLinks(TextLinks textLinks) {
+ mTextLinks = textLinks;
+ }
+
+ /**
* Turn this item into text, regardless of the type of data it
* actually contains.
*
@@ -1183,6 +1209,7 @@
dest.writeTypedObject(item.mIntent, flags);
dest.writeTypedObject(item.mUri, flags);
dest.writeTypedObject(item.mActivityInfo, flags);
+ dest.writeTypedObject(item.mTextLinks, flags);
}
}
@@ -1201,8 +1228,10 @@
Intent intent = in.readTypedObject(Intent.CREATOR);
Uri uri = in.readTypedObject(Uri.CREATOR);
ActivityInfo info = in.readTypedObject(ActivityInfo.CREATOR);
+ TextLinks textLinks = in.readTypedObject(TextLinks.CREATOR);
Item item = new Item(text, htmlText, intent, uri);
item.setActivityInfo(info);
+ item.setTextLinks(textLinks);
mItems.add(item);
}
}
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
index d48f832..f49362e 100644
--- a/core/java/android/content/ClipDescription.java
+++ b/core/java/android/content/ClipDescription.java
@@ -16,16 +16,25 @@
package android.content;
+import android.annotation.FloatRange;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Map;
/**
* Meta-data describing the contents of a {@link ClipData}. Provides enough
@@ -115,12 +124,39 @@
*/
public static final String EXTRA_ACTIVITY_OPTIONS = "android.intent.extra.ACTIVITY_OPTIONS";
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value =
+ { CLASSIFICATION_NOT_COMPLETE, CLASSIFICATION_NOT_PERFORMED, CLASSIFICATION_COMPLETE})
+ @interface ClassificationStatus {}
+
+ /**
+ * Value returned by {@link #getConfidenceScore(String)} if text classification has not been
+ * completed on the associated clip. This will be always be the case if the clip has not been
+ * copied to clipboard, or if there is no associated clip.
+ */
+ public static final int CLASSIFICATION_NOT_COMPLETE = 1;
+
+ /**
+ * Value returned by {@link #getConfidenceScore(String)} if text classification was not and will
+ * not be performed on the associated clip. This may be the case if the clip does not contain
+ * text in its first item, or if the text is too long.
+ */
+ public static final int CLASSIFICATION_NOT_PERFORMED = 2;
+
+ /**
+ * Value returned by {@link #getConfidenceScore(String)} if text classification has been
+ * completed.
+ */
+ public static final int CLASSIFICATION_COMPLETE = 3;
final CharSequence mLabel;
private final ArrayList<String> mMimeTypes;
private PersistableBundle mExtras;
private long mTimeStamp;
private boolean mIsStyledText;
+ private final ArrayMap<String, Float> mEntityConfidence = new ArrayMap<>();
+ private int mClassificationStatus = CLASSIFICATION_NOT_COMPLETE;
/**
* Create a new clip.
@@ -346,6 +382,61 @@
mIsStyledText = isStyledText;
}
+ /**
+ * Sets the current status of text classification for the associated clip.
+ *
+ * @hide
+ */
+ public void setClassificationStatus(@ClassificationStatus int status) {
+ mClassificationStatus = status;
+ }
+
+ /**
+ * Returns a score indicating confidence that an instance of the given entity is present in the
+ * first item of the clip data, if that item is plain text and text classification has been
+ * performed. The value ranges from 0 (low confidence) to 1 (high confidence). 0 indicates that
+ * the entity was not found in the classified text.
+ *
+ * <p>Entities should be as defined in the {@link TextClassifier} class, such as
+ * {@link TextClassifier#TYPE_ADDRESS}, {@link TextClassifier#TYPE_URL}, or
+ * {@link TextClassifier#TYPE_EMAIL}.
+ *
+ * <p>If the result is positive for any entity, the full classification result as a
+ * {@link TextLinks} object may be obtained using the {@link ClipData.Item#getTextLinks()}
+ * method.
+ *
+ * @throws IllegalStateException if {@link #getClassificationStatus()} is not
+ * {@link #CLASSIFICATION_COMPLETE}
+ */
+ @FloatRange(from = 0.0, to = 1.0)
+ public float getConfidenceScore(@NonNull @TextClassifier.EntityType String entity) {
+ if (mClassificationStatus != CLASSIFICATION_COMPLETE) {
+ throw new IllegalStateException("Classification not complete");
+ }
+ return mEntityConfidence.getOrDefault(entity, 0f);
+ }
+
+ /**
+ * Returns {@link #CLASSIFICATION_COMPLETE} if text classification has been performed on the
+ * associated {@link ClipData}. If this is the case then {@link #getConfidenceScore} may be used
+ * to retrieve information about entities within the text. Otherwise, returns
+ * {@link #CLASSIFICATION_NOT_COMPLETE} if classification has not yet returned results, or
+ * {@link #CLASSIFICATION_NOT_PERFORMED} if classification was not attempted (e.g. because the
+ * text was too long).
+ */
+ public @ClassificationStatus int getClassificationStatus() {
+ return mClassificationStatus;
+ }
+
+ /**
+ * @hide
+ */
+ public void setConfidenceScores(Map<String, Float> confidences) {
+ mEntityConfidence.clear();
+ mEntityConfidence.putAll(confidences);
+ mClassificationStatus = CLASSIFICATION_COMPLETE;
+ }
+
@Override
public String toString() {
StringBuilder b = new StringBuilder(128);
@@ -451,6 +542,23 @@
dest.writePersistableBundle(mExtras);
dest.writeLong(mTimeStamp);
dest.writeBoolean(mIsStyledText);
+ dest.writeInt(mClassificationStatus);
+ dest.writeBundle(confidencesToBundle());
+ }
+
+ private Bundle confidencesToBundle() {
+ Bundle bundle = new Bundle();
+ int size = mEntityConfidence.size();
+ for (int i = 0; i < size; i++) {
+ bundle.putFloat(mEntityConfidence.keyAt(i), mEntityConfidence.valueAt(i));
+ }
+ return bundle;
+ }
+
+ private void readBundleToConfidences(Bundle bundle) {
+ for (String key : bundle.keySet()) {
+ mEntityConfidence.put(key, bundle.getFloat(key));
+ }
}
ClipDescription(Parcel in) {
@@ -459,6 +567,8 @@
mExtras = in.readPersistableBundle();
mTimeStamp = in.readLong();
mIsStyledText = in.readBoolean();
+ mClassificationStatus = in.readInt();
+ readBundleToConfidences(in.readBundle());
}
public static final @android.annotation.NonNull Parcelable.Creator<ClipDescription> CREATOR =
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 49248b5..73b4f62 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -2621,6 +2621,48 @@
return !TextUtils.isEmpty(uri.getUserInfo());
}
+ /**
+ * Returns the given content URI explicitly associated with the given {@link UserHandle}.
+ *
+ * @param contentUri The content URI to be associated with a user handle.
+ * @param userHandle The user handle with which to associate the URI.
+ *
+ * @throws IllegalArgumentException if
+ * <ul>
+ * <li>the given URI is not content URI (a content URI has {@link Uri#getScheme} equal to
+ * {@link ContentResolver.SCHEME_CONTENT}) or</li>
+ * <li>the given URI is already explicitly associated with a {@link UserHandle}, which is
+ * different than the given one.</li>
+ * </ul>
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static Uri createContentUriAsUser(
+ @NonNull Uri contentUri, @NonNull UserHandle userHandle) {
+ if (!ContentResolver.SCHEME_CONTENT.equals(contentUri.getScheme())) {
+ throw new IllegalArgumentException(String.format(
+ "Given URI [%s] is not a content URI: ", contentUri));
+ }
+
+ int userId = userHandle.getIdentifier();
+ if (uriHasUserId(contentUri)) {
+ if (String.valueOf(userId).equals(contentUri.getUserInfo())) {
+ return contentUri;
+ }
+ throw new IllegalArgumentException(String.format(
+ "Given URI [%s] already has a user ID, different from given user handle [%s]",
+ contentUri,
+ userId));
+ }
+
+ Uri.Builder builder = contentUri.buildUpon();
+ builder.encodedAuthority(
+ "" + userHandle.getIdentifier() + "@" + contentUri.getEncodedAuthority());
+ return builder.build();
+ }
+
/** @hide */
@UnsupportedAppUsage
public static Uri maybeAddUserId(Uri uri, int userId) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2523459..f8dd0e1 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3531,6 +3531,7 @@
VIBRATOR_SERVICE,
//@hide: STATUS_BAR_SERVICE,
CONNECTIVITY_SERVICE,
+ PAC_PROXY_SERVICE,
VCN_MANAGEMENT_SERVICE,
//@hide: IP_MEMORY_STORE_SERVICE,
IPSEC_SERVICE,
@@ -4137,6 +4138,17 @@
public static final String CONNECTIVITY_SERVICE = "connectivity";
/**
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
+ * android.net.PacProxyManager} for handling management of
+ * pac proxy information.
+ *
+ * @see #getSystemService(String)
+ * @see android.net.PacProxyManager
+ * @hide
+ */
+ public static final String PAC_PROXY_SERVICE = "pac_proxy";
+
+ /**
* Use with {@link #getSystemService(String)} to retrieve a {@link android.net.vcn.VcnManager}
* for managing Virtual Carrier Networks
*
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c601aab..adf9ff3 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1901,6 +1901,20 @@
"android.intent.action.AUTO_REVOKE_PERMISSIONS";
/**
+ * Activity action: Launch UI to manage unused apps (hibernated apps).
+ *
+ * <p>
+ * Input: Nothing.
+ * </p>
+ * <p>
+ * Output: Nothing.
+ * </p>
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_MANAGE_UNUSED_APPS =
+ "android.intent.action.MANAGE_UNUSED_APPS";
+
+ /**
* Activity action: Launch UI to review permissions for an app.
* The system uses this intent if permission review for apps not
* supporting the new runtime permissions model is enabled. In
@@ -1940,8 +1954,8 @@
/**
* Activity action: Launch UI to show information about the usage
- * of a given permission. This action would be handled by apps that
- * want to show details about how and why given permission is being
+ * of a given permission group. This action would be handled by apps that
+ * want to show details about how and why given permission group is being
* used.
* <p>
* <strong>Important:</strong>You must protect the activity that handles
@@ -1951,7 +1965,7 @@
* activities that are not properly protected.
*
* <p>
- * Input: {@code android.intent.extra.PERMISSION_NAME} specifies the permission
+ * Input: {@link android.Manifest.permission_group} specifies the permission group
* for which the launched UI would be targeted.
* </p>
* <p>
diff --git a/core/java/android/hardware/biometrics/SensorPropertiesInternal.java b/core/java/android/hardware/biometrics/SensorPropertiesInternal.java
index 0b81c6c..909f456 100644
--- a/core/java/android/hardware/biometrics/SensorPropertiesInternal.java
+++ b/core/java/android/hardware/biometrics/SensorPropertiesInternal.java
@@ -31,23 +31,31 @@
public final int sensorId;
@SensorProperties.Strength public final int sensorStrength;
public final int maxEnrollmentsPerUser;
+ public final boolean resetLockoutRequiresHardwareAuthToken;
+ public final boolean resetLockoutRequiresChallenge;
public static SensorPropertiesInternal from(@NonNull SensorPropertiesInternal prop) {
return new SensorPropertiesInternal(prop.sensorId, prop.sensorStrength,
- prop.maxEnrollmentsPerUser);
+ prop.maxEnrollmentsPerUser, prop.resetLockoutRequiresHardwareAuthToken,
+ prop.resetLockoutRequiresChallenge);
}
protected SensorPropertiesInternal(int sensorId, @SensorProperties.Strength int sensorStrength,
- int maxEnrollmentsPerUser) {
+ int maxEnrollmentsPerUser, boolean resetLockoutRequiresHardwareAuthToken,
+ boolean resetLockoutRequiresChallenge) {
this.sensorId = sensorId;
this.sensorStrength = sensorStrength;
this.maxEnrollmentsPerUser = maxEnrollmentsPerUser;
+ this.resetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken;
+ this.resetLockoutRequiresChallenge = resetLockoutRequiresChallenge;
}
protected SensorPropertiesInternal(Parcel in) {
sensorId = in.readInt();
sensorStrength = in.readInt();
maxEnrollmentsPerUser = in.readInt();
+ resetLockoutRequiresHardwareAuthToken = in.readBoolean();
+ resetLockoutRequiresChallenge = in.readBoolean();
}
public static final Creator<SensorPropertiesInternal> CREATOR =
@@ -73,6 +81,8 @@
dest.writeInt(sensorId);
dest.writeInt(sensorStrength);
dest.writeInt(maxEnrollmentsPerUser);
+ dest.writeBoolean(resetLockoutRequiresHardwareAuthToken);
+ dest.writeBoolean(resetLockoutRequiresChallenge);
}
@Override
diff --git a/core/java/android/hardware/display/BrightnessChangeEvent.java b/core/java/android/hardware/display/BrightnessChangeEvent.java
index a6c6b46..6b7d8c3 100644
--- a/core/java/android/hardware/display/BrightnessChangeEvent.java
+++ b/core/java/android/hardware/display/BrightnessChangeEvent.java
@@ -47,6 +47,10 @@
* @hide */
public final int userId;
+ /** The unique id of the screen on which the brightness was changed */
+ @NonNull
+ public final String uniqueDisplayId;
+
/** Lux values of recent sensor data */
public final float[] luxValues;
@@ -120,15 +124,16 @@
/** @hide */
private BrightnessChangeEvent(float brightness, long timeStamp, String packageName,
- int userId, float[] luxValues, long[] luxTimestamps, float batteryLevel,
- float powerBrightnessFactor, boolean nightMode, int colorTemperature,
- boolean reduceBrightColors, int reduceBrightColorsStrength,
+ int userId, String uniqueDisplayId, float[] luxValues, long[] luxTimestamps,
+ float batteryLevel, float powerBrightnessFactor, boolean nightMode,
+ int colorTemperature, boolean reduceBrightColors, int reduceBrightColorsStrength,
float reduceBrightColorsOffset, float lastBrightness, boolean isDefaultBrightnessConfig,
boolean isUserSetBrightness, long[] colorValueBuckets, long colorSampleDuration) {
this.brightness = brightness;
this.timeStamp = timeStamp;
this.packageName = packageName;
this.userId = userId;
+ this.uniqueDisplayId = uniqueDisplayId;
this.luxValues = luxValues;
this.luxTimestamps = luxTimestamps;
this.batteryLevel = batteryLevel;
@@ -151,6 +156,7 @@
this.timeStamp = other.timeStamp;
this.packageName = redactPackage ? null : other.packageName;
this.userId = other.userId;
+ this.uniqueDisplayId = other.uniqueDisplayId;
this.luxValues = other.luxValues;
this.luxTimestamps = other.luxTimestamps;
this.batteryLevel = other.batteryLevel;
@@ -172,6 +178,7 @@
timeStamp = source.readLong();
packageName = source.readString();
userId = source.readInt();
+ uniqueDisplayId = source.readString();
luxValues = source.createFloatArray();
luxTimestamps = source.createLongArray();
batteryLevel = source.readFloat();
@@ -209,6 +216,7 @@
dest.writeLong(timeStamp);
dest.writeString(packageName);
dest.writeInt(userId);
+ dest.writeString(uniqueDisplayId);
dest.writeFloatArray(luxValues);
dest.writeLongArray(luxTimestamps);
dest.writeFloat(batteryLevel);
@@ -231,6 +239,7 @@
private long mTimeStamp;
private String mPackageName;
private int mUserId;
+ private String mUniqueDisplayId;
private float[] mLuxValues;
private long[] mLuxTimestamps;
private float mBatteryLevel;
@@ -270,6 +279,12 @@
return this;
}
+ /** {@see BrightnessChangeEvent#uniqueScreenId} */
+ public Builder setUniqueDisplayId(String uniqueId) {
+ mUniqueDisplayId = uniqueId;
+ return this;
+ }
+
/** {@see BrightnessChangeEvent#luxValues} */
public Builder setLuxValues(float[] luxValues) {
mLuxValues = luxValues;
@@ -354,11 +369,11 @@
/** Builds a BrightnessChangeEvent */
public BrightnessChangeEvent build() {
return new BrightnessChangeEvent(mBrightness, mTimeStamp,
- mPackageName, mUserId, mLuxValues, mLuxTimestamps, mBatteryLevel,
- mPowerBrightnessFactor, mNightMode, mColorTemperature, mReduceBrightColors,
- mReduceBrightColorsStrength, mReduceBrightColorsOffset, mLastBrightness,
- mIsDefaultBrightnessConfig, mIsUserSetBrightness, mColorValueBuckets,
- mColorSampleDuration);
+ mPackageName, mUserId, mUniqueDisplayId, mLuxValues, mLuxTimestamps,
+ mBatteryLevel, mPowerBrightnessFactor, mNightMode, mColorTemperature,
+ mReduceBrightColors, mReduceBrightColorsStrength, mReduceBrightColorsOffset,
+ mLastBrightness, mIsDefaultBrightnessConfig, mIsUserSetBrightness,
+ mColorValueBuckets, mColorSampleDuration);
}
}
}
diff --git a/core/java/android/hardware/face/FaceSensorPropertiesInternal.java b/core/java/android/hardware/face/FaceSensorPropertiesInternal.java
index b9c0d12..34cbcb4 100644
--- a/core/java/android/hardware/face/FaceSensorPropertiesInternal.java
+++ b/core/java/android/hardware/face/FaceSensorPropertiesInternal.java
@@ -41,8 +41,11 @@
*/
public FaceSensorPropertiesInternal(int sensorId, @SensorProperties.Strength int strength,
int maxEnrollmentsPerUser, boolean supportsFaceDetection,
- boolean supportsSelfIllumination) {
- super(sensorId, strength, maxEnrollmentsPerUser);
+ boolean supportsSelfIllumination, boolean resetLockoutRequiresChallenge) {
+ // resetLockout is managed by the HAL and requires a HardwareAuthToken for all face
+ // HAL interfaces (IBiometricsFace@1.0 HIDL and IFace@1.0 AIDL).
+ super(sensorId, strength, maxEnrollmentsPerUser,
+ true /* resetLockoutRequiresHardwareAuthToken */, resetLockoutRequiresChallenge);
this.supportsFaceDetection = supportsFaceDetection;
this.supportsSelfIllumination = supportsSelfIllumination;
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
index 51addc9..adc61a7 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
@@ -36,12 +36,6 @@
public final @FingerprintSensorProperties.SensorType int sensorType;
/**
- * IBiometricsFingerprint@2.1 does not manage timeout below the HAL, so the Gatekeeper HAT
- * cannot be checked
- */
- public final boolean resetLockoutRequiresHardwareAuthToken;
-
- /**
* The location of the center of the sensor if applicable. For example, sensors of type
* {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the
* distance in pixels, measured from the left edge of the screen.
@@ -68,9 +62,13 @@
@FingerprintSensorProperties.SensorType int sensorType,
boolean resetLockoutRequiresHardwareAuthToken, int sensorLocationX, int sensorLocationY,
int sensorRadius) {
- super(sensorId, strength, maxEnrollmentsPerUser);
+ // IBiometricsFingerprint@2.1 handles lockout in the framework, so the challenge is not
+ // required as it can only be generated/attested/verified by TEE components.
+ // IFingerprint@1.0 handles lockout below the HAL, but does not require a challenge. See
+ // the HAL interface for more details.
+ super(sensorId, strength, maxEnrollmentsPerUser, resetLockoutRequiresHardwareAuthToken,
+ false /* resetLockoutRequiresChallenge */);
this.sensorType = sensorType;
- this.resetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken;
this.sensorLocationX = sensorLocationX;
this.sensorLocationY = sensorLocationY;
this.sensorRadius = sensorRadius;
@@ -98,9 +96,9 @@
@SensorProperties.Strength int strength, int maxEnrollmentsPerUser,
@FingerprintSensorProperties.SensorType int sensorType,
boolean resetLockoutRequiresHardwareAuthToken) {
- super(sensorId, strength, maxEnrollmentsPerUser);
+ super(sensorId, strength, maxEnrollmentsPerUser, resetLockoutRequiresHardwareAuthToken,
+ false /* resetLockoutRequiresChallenge */);
this.sensorType = sensorType;
- this.resetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken;
int[] props = context.getResources().getIntArray(
com.android.internal.R.array.config_udfps_sensor_props);
@@ -119,7 +117,6 @@
protected FingerprintSensorPropertiesInternal(Parcel in) {
super(in);
sensorType = in.readInt();
- resetLockoutRequiresHardwareAuthToken = in.readBoolean();
sensorLocationX = in.readInt();
sensorLocationY = in.readInt();
sensorRadius = in.readInt();
@@ -147,7 +144,6 @@
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(sensorType);
- dest.writeBoolean(resetLockoutRequiresHardwareAuthToken);
dest.writeInt(sensorLocationX);
dest.writeInt(sensorLocationY);
dest.writeInt(sensorRadius);
diff --git a/core/java/android/net/IPacProxyInstalledListener.aidl b/core/java/android/net/IPacProxyInstalledListener.aidl
new file mode 100644
index 0000000..b1f946e
--- /dev/null
+++ b/core/java/android/net/IPacProxyInstalledListener.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.Network;
+import android.net.ProxyInfo;
+
+/** {@hide} */
+oneway interface IPacProxyInstalledListener {
+ void onPacProxyInstalled(in Network network, in ProxyInfo proxy);
+}
diff --git a/core/java/android/net/IPacProxyManager.aidl b/core/java/android/net/IPacProxyManager.aidl
new file mode 100644
index 0000000..8f65c56
--- /dev/null
+++ b/core/java/android/net/IPacProxyManager.aidl
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing perNmissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.IPacProxyInstalledListener;
+import android.net.ProxyInfo;
+
+/** {@hide} */
+interface IPacProxyManager
+{
+ void addListener(IPacProxyInstalledListener listener);
+ void removeListener(IPacProxyInstalledListener listener);
+ void setCurrentProxyScriptUrl(in ProxyInfo proxyInfo);
+}
diff --git a/core/java/android/net/PacProxyManager.java b/core/java/android/net/PacProxyManager.java
new file mode 100644
index 0000000..8f7ad8c
--- /dev/null
+++ b/core/java/android/net/PacProxyManager.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 android.net;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.Binder;
+import android.os.RemoteException;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+@SystemService(Context.PAC_PROXY_SERVICE)
+public class PacProxyManager {
+ private final Context mContext;
+ private final IPacProxyManager mService;
+ @GuardedBy("mListenerMap")
+ private final HashMap<PacProxyInstalledListener, PacProxyInstalledListenerProxy>
+ mListenerMap = new HashMap<>();
+
+ /** @hide */
+ public PacProxyManager(Context context, IPacProxyManager service) {
+ Objects.requireNonNull(service, "missing IPacProxyManager");
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Add a listener to start monitoring events reported by PacProxyService.
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_SETTINGS})
+ public void addPacProxyInstalledListener(@NonNull Executor executor,
+ @NonNull PacProxyInstalledListener listener) {
+ try {
+ synchronized (mListenerMap) {
+ final PacProxyInstalledListenerProxy listenerProxy =
+ new PacProxyInstalledListenerProxy(executor, listener);
+
+ if (null != mListenerMap.putIfAbsent(listener, listenerProxy)) {
+ throw new IllegalStateException("Listener is already added.");
+ }
+ mService.addListener(listenerProxy);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Remove the listener to stop monitoring the event of PacProxyInstalledListener.
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_SETTINGS})
+ public void removePacProxyInstalledListener(@NonNull PacProxyInstalledListener listener) {
+ try {
+ synchronized (mListenerMap) {
+ final PacProxyInstalledListenerProxy listenerProxy = mListenerMap.remove(listener);
+ if (listenerProxy == null) return;
+ mService.removeListener(listenerProxy);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Updates the PAC Proxy Installer with current Proxy information.
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_SETTINGS})
+ public void setCurrentProxyScriptUrl(@Nullable ProxyInfo proxy) {
+ try {
+ mService.setCurrentProxyScriptUrl(proxy);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * A callback interface for monitoring changes of PAC proxy information.
+ */
+ public interface PacProxyInstalledListener {
+ /**
+ * Notify that the PAC proxy has been installed. Note that this method will be called with
+ * a ProxyInfo with an empty PAC URL when the PAC proxy is removed.
+ *
+ * This method supports different PAC proxies per-network but not all devices might support
+ * per-network proxies. In that case it will be applied globally.
+ *
+ * @param network the network for which this proxy installed.
+ * @param proxy the installed proxy.
+ */
+ void onPacProxyInstalled(@Nullable Network network, @NonNull ProxyInfo proxy);
+ }
+
+ /**
+ * PacProxyInstalledListener proxy for PacProxyInstalledListener object.
+ * @hide
+ */
+ public class PacProxyInstalledListenerProxy extends IPacProxyInstalledListener.Stub {
+ private final Executor mExecutor;
+ private final PacProxyInstalledListener mListener;
+
+ PacProxyInstalledListenerProxy(Executor executor, PacProxyInstalledListener listener) {
+ mExecutor = executor;
+ mListener = listener;
+ }
+
+ @Override
+ public void onPacProxyInstalled(Network network, ProxyInfo proxy) {
+ Binder.withCleanCallingIdentity(() -> {
+ mExecutor.execute(() -> {
+ mListener.onPacProxyInstalled(network, proxy);
+ });
+ });
+ }
+ }
+}
diff --git a/core/java/android/net/PacProxySelector.java b/core/java/android/net/PacProxySelector.java
index 326943a..84b7eec 100644
--- a/core/java/android/net/PacProxySelector.java
+++ b/core/java/android/net/PacProxySelector.java
@@ -51,7 +51,7 @@
ServiceManager.getService(PROXY_SERVICE));
if (mProxyService == null) {
// Added because of b10267814 where mako is restarting.
- Log.e(TAG, "PacProxyInstaller: no proxy service");
+ Log.e(TAG, "PacProxyService: no proxy service");
}
mDefaultList = Lists.newArrayList(java.net.Proxy.NO_PROXY);
}
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index df4ade0..d89c3d5 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -21,6 +21,7 @@
import android.util.Slog;
import java.io.PrintWriter;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
@@ -354,6 +355,23 @@
}
/**
+ * Performs {@code action} on each callback and associated cookie, calling {@link
+ * #beginBroadcast()}/{@link #finishBroadcast()} before/after looping.
+ *
+ * @hide
+ */
+ public <C> void broadcast(BiConsumer<E, C> action) {
+ int itemCount = beginBroadcast();
+ try {
+ for (int i = 0; i < itemCount; i++) {
+ action.accept(getBroadcastItem(i), (C) getBroadcastCookie(i));
+ }
+ } finally {
+ finishBroadcast();
+ }
+ }
+
+ /**
* Returns the number of registered callbacks. Note that the number of registered
* callbacks may differ from the value returned by {@link #beginBroadcast()} since
* the former returns the number of callbacks registered at the time of the call
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6865041..5d139d9 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9877,6 +9877,14 @@
"reminder_exp_learning_event_count";
/**
+ * Whether to show clipboard access notifications.
+ *
+ * @hide
+ */
+ public static final String CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS =
+ "clipboard_show_access_notifications";
+
+ /**
* These entries are considered common between the personal and the managed profile,
* since the managed profile doesn't get to change them.
*/
diff --git a/core/java/android/service/displayhash/DisplayHashParams.aidl b/core/java/android/service/displayhash/DisplayHashParams.aidl
new file mode 100644
index 0000000..90f9bf1
--- /dev/null
+++ b/core/java/android/service/displayhash/DisplayHashParams.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.service.displayhash;
+
+parcelable DisplayHashParams;
diff --git a/core/java/android/service/displayhash/DisplayHashParams.java b/core/java/android/service/displayhash/DisplayHashParams.java
new file mode 100644
index 0000000..6a176a33
--- /dev/null
+++ b/core/java/android/service/displayhash/DisplayHashParams.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.service.displayhash;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
+import android.os.Parcelable;
+import android.util.Size;
+import android.view.displayhash.DisplayHashResultCallback;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Information passed from the {@link DisplayHasherService} to system server about how to get the
+ * display data that will be used to generate the {@link android.view.displayhash.DisplayHash}
+ *
+ * @hide
+ */
+@SystemApi
+@DataClass(genAidl = true, genToString = true, genParcelable = true, genHiddenConstructor = true)
+public final class DisplayHashParams implements Parcelable {
+ /**
+ * The size to scale the buffer to so the hash algorithm can properly generate the hash. The
+ * buffer given to the {@link DisplayHasherService#onGenerateDisplayHash(byte[], HardwareBuffer,
+ * Rect, String, DisplayHashResultCallback)} will be stretched based on the value set here.
+ * If {@code null}, the buffer size will not be changed.
+ */
+ @Nullable
+ private final Size mBufferSize;
+
+ /**
+ * Whether the content captured will use filtering when scaling.
+ */
+ private final boolean mBufferScaleWithFiltering;
+
+ /**
+ * Whether the content will be captured in grayscale or color.
+ */
+ private final boolean mGrayscaleBuffer;
+
+ /**
+ * A builder for {@link DisplayHashParams}
+ */
+ public static final class Builder {
+ @Nullable
+ private Size mBufferSize;
+ private boolean mBufferScaleWithFiltering;
+ private boolean mGrayscaleBuffer;
+
+ /**
+ * Creates a new Builder.
+ */
+ public Builder() {
+ }
+
+ /**
+ * The size to scale the buffer to so the hash algorithm can properly generate the hash.
+ */
+ @NonNull
+ public Builder setBufferSize(int w, int h) {
+ mBufferSize = new Size(w, h);
+ return this;
+ }
+
+ /**
+ * Whether the content captured will use filtering when scaling.
+ */
+ @NonNull
+ public Builder setBufferScaleWithFiltering(boolean value) {
+ mBufferScaleWithFiltering = value;
+ return this;
+ }
+
+ /**
+ * Whether the content will be captured in grayscale or color.
+ */
+ @NonNull
+ public Builder setGrayscaleBuffer(boolean value) {
+ mGrayscaleBuffer = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ @NonNull
+ public DisplayHashParams build() {
+ return new DisplayHashParams(mBufferSize, mBufferScaleWithFiltering, mGrayscaleBuffer);
+ }
+ }
+
+
+
+ // 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/service/displayhash/DisplayHashParams.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new DisplayHashParams.
+ *
+ * @param bufferSize
+ * The size to scale the buffer to so the hash algorithm can properly generate the hash. The
+ * buffer given to the {@link DisplayHasherService#onGenerateDisplayHash(byte[], HardwareBuffer,
+ * Rect, String, DisplayHashResultCallback)} will be stretched based on the value set here.
+ * If {@code null}, the buffer size will not be changed.
+ * @param bufferScaleWithFiltering
+ * Whether the content captured will use filtering when scaling.
+ * @param grayscaleBuffer
+ * Whether the content will be captured in grayscale or color.
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public DisplayHashParams(
+ @Nullable Size bufferSize,
+ boolean bufferScaleWithFiltering,
+ boolean grayscaleBuffer) {
+ this.mBufferSize = bufferSize;
+ this.mBufferScaleWithFiltering = bufferScaleWithFiltering;
+ this.mGrayscaleBuffer = grayscaleBuffer;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The size to scale the buffer to so the hash algorithm can properly generate the hash. The
+ * buffer given to the {@link DisplayHasherService#onGenerateDisplayHash(byte[], HardwareBuffer,
+ * Rect, String, DisplayHashResultCallback)} will be stretched based on the value set here.
+ * If {@code null}, the buffer size will not be changed.
+ */
+ @DataClass.Generated.Member
+ public @Nullable Size getBufferSize() {
+ return mBufferSize;
+ }
+
+ /**
+ * Whether the content captured will use filtering when scaling.
+ */
+ @DataClass.Generated.Member
+ public boolean isBufferScaleWithFiltering() {
+ return mBufferScaleWithFiltering;
+ }
+
+ /**
+ * Whether the content will be captured in grayscale or color.
+ */
+ @DataClass.Generated.Member
+ public boolean isGrayscaleBuffer() {
+ return mGrayscaleBuffer;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "DisplayHashParams { " +
+ "bufferSize = " + mBufferSize + ", " +
+ "bufferScaleWithFiltering = " + mBufferScaleWithFiltering + ", " +
+ "grayscaleBuffer = " + mGrayscaleBuffer +
+ " }";
+ }
+
+ @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 (mBufferScaleWithFiltering) flg |= 0x2;
+ if (mGrayscaleBuffer) flg |= 0x4;
+ if (mBufferSize != null) flg |= 0x1;
+ dest.writeByte(flg);
+ if (mBufferSize != null) dest.writeSize(mBufferSize);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ DisplayHashParams(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ boolean bufferScaleWithFiltering = (flg & 0x2) != 0;
+ boolean grayscaleBuffer = (flg & 0x4) != 0;
+ Size bufferSize = (flg & 0x1) == 0 ? null : (Size) in.readSize();
+
+ this.mBufferSize = bufferSize;
+ this.mBufferScaleWithFiltering = bufferScaleWithFiltering;
+ this.mGrayscaleBuffer = grayscaleBuffer;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<DisplayHashParams> CREATOR
+ = new Parcelable.Creator<DisplayHashParams>() {
+ @Override
+ public DisplayHashParams[] newArray(int size) {
+ return new DisplayHashParams[size];
+ }
+
+ @Override
+ public DisplayHashParams createFromParcel(@NonNull android.os.Parcel in) {
+ return new DisplayHashParams(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1615565493989L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/service/displayhash/DisplayHashParams.java",
+ inputSignatures = "private final @android.annotation.Nullable android.util.Size mBufferSize\nprivate final boolean mBufferScaleWithFiltering\nprivate final boolean mGrayscaleBuffer\nclass DisplayHashParams extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.Nullable android.util.Size mBufferSize\nprivate boolean mBufferScaleWithFiltering\nprivate boolean mGrayscaleBuffer\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setBufferSize(int,int)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setBufferScaleWithFiltering(boolean)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setGrayscaleBuffer(boolean)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genAidl=true, genToString=true, genParcelable=true, genHiddenConstructor=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/service/displayhash/DisplayHasherService.java b/core/java/android/service/displayhash/DisplayHasherService.java
index 331dbe9..2105d84 100644
--- a/core/java/android/service/displayhash/DisplayHasherService.java
+++ b/core/java/android/service/displayhash/DisplayHasherService.java
@@ -34,6 +34,8 @@
import android.view.displayhash.DisplayHashResultCallback;
import android.view.displayhash.VerifiedDisplayHash;
+import java.util.Map;
+
/**
* A service that handles generating and verify {@link DisplayHash}.
*
@@ -50,15 +52,6 @@
"android.service.displayhash.extra.VERIFIED_DISPLAY_HASH";
/**
- * Manifest metadata key for the resource string array containing the names of all hashing
- * algorithms provided by the service.
- *
- * @hide
- */
- public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS =
- "android.displayhash.available_algorithms";
-
- /**
* The {@link Intent} action that must be declared as handled by a service in its manifest
* for the system to recognize it as a DisplayHash providing service.
*
@@ -96,7 +89,7 @@
* @param buffer The buffer for the content to generate the hash for.
* @param bounds The size and position of the content in window space.
* @param hashAlgorithm The String for the hashing algorithm to use based values in
- * {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS)}.
+ * {@link #getDisplayHashAlgorithms(RemoteCallback)}.
* @param callback The callback to invoke
* {@link DisplayHashResultCallback#onDisplayHashResult(DisplayHash)}
* if successfully generated a DisplayHash or {@link
@@ -108,6 +101,12 @@
@NonNull String hashAlgorithm, @NonNull DisplayHashResultCallback callback);
/**
+ * Returns a map of supported algorithms and their {@link DisplayHashParams}
+ */
+ @NonNull
+ public abstract Map<String, DisplayHashParams> onGetDisplayHashAlgorithms();
+
+ /**
* Call to verify that the DisplayHash passed in was generated by the system.
*
* @param salt The salt value to use when verifying the hmac. This should be the
@@ -132,6 +131,15 @@
callback.sendResult(data);
}
+ private void getDisplayHashAlgorithms(RemoteCallback callback) {
+ Map<String, DisplayHashParams> displayHashParams = onGetDisplayHashAlgorithms();
+ final Bundle data = new Bundle();
+ for (Map.Entry<String, DisplayHashParams> entry : displayHashParams.entrySet()) {
+ data.putParcelable(entry.getKey(), entry.getValue());
+ }
+ callback.sendResult(data);
+ }
+
private final class DisplayHasherServiceWrapper extends IDisplayHasherService.Stub {
@Override
public void generateDisplayHash(byte[] salt, HardwareBuffer buffer, Rect bounds,
@@ -164,5 +172,11 @@
obtainMessage(DisplayHasherService::verifyDisplayHash,
DisplayHasherService.this, salt, displayHash, callback));
}
+
+ @Override
+ public void getDisplayHashAlgorithms(RemoteCallback callback) {
+ mHandler.sendMessage(obtainMessage(DisplayHasherService::getDisplayHashAlgorithms,
+ DisplayHasherService.this, callback));
+ }
}
}
diff --git a/core/java/android/service/displayhash/IDisplayHasherService.aidl b/core/java/android/service/displayhash/IDisplayHasherService.aidl
index 236bc28..d9dcdca 100644
--- a/core/java/android/service/displayhash/IDisplayHasherService.aidl
+++ b/core/java/android/service/displayhash/IDisplayHasherService.aidl
@@ -51,4 +51,11 @@
* @param callback The callback invoked to send back the VerifiedDisplayHash.
*/
void verifyDisplayHash(in byte[] salt, in DisplayHash displayHash, in RemoteCallback callback);
+
+ /**
+ * Call to get a map of supported algorithms and their {@link DisplayHashParams}
+ *
+ * @param callback The callback invoked to send back the map of algorithms to DisplayHashParams.
+ */
+ void getDisplayHashAlgorithms(in RemoteCallback callback);
}
diff --git a/core/java/android/service/timezone/TimeZoneProviderService.java b/core/java/android/service/timezone/TimeZoneProviderService.java
index d71a830..a9348c6 100644
--- a/core/java/android/service/timezone/TimeZoneProviderService.java
+++ b/core/java/android/service/timezone/TimeZoneProviderService.java
@@ -218,7 +218,30 @@
}
/**
- * Starts the provider sending updates.
+ * Informs the provider that it should start detecting and reporting the detected time zone
+ * state via the various {@code report} methods. Implementations of {@link
+ * #onStartUpdates(long)} should return immediately, and will typically be used to start
+ * worker threads or begin asynchronous location listening.
+ *
+ * <p>Between {@link #onStartUpdates(long)} and {@link #onStopUpdates()} calls, the Android
+ * system server holds the latest report from the provider in memory. After an initial report,
+ * provider implementations are only required to send a report via {@link
+ * #reportSuggestion(TimeZoneProviderSuggestion)} or via {@link #reportUncertain()} when it
+ * differs from the previous report.
+ *
+ * <p>{@link #reportPermanentFailure(Throwable)} can also be called by provider implementations
+ * in rare cases, after which the provider should consider itself stopped and not make any
+ * further reports. {@link #onStopUpdates()} will not be called in this case.
+ *
+ * <p>The {@code initializationTimeoutMillis} parameter indicates how long the provider has been
+ * granted to call one of the {@code report} methods for the first time. If the provider does
+ * not call one of the {@code report} methods in this time, it may be judged uncertain and the
+ * Android system server may move on to use other providers or detection methods. Providers
+ * should therefore make best efforts during this time to generate a report, which could involve
+ * increased power usage. Providers should preferably report an explicit {@link
+ * #reportUncertain()} if the time zone(s) cannot be detected within the initialization timeout.
+ *
+ * @see #onStopUpdates() for the signal from the system server to stop sending reports
*/
public abstract void onStartUpdates(@DurationMillisLong long initializationTimeoutMillis);
@@ -228,7 +251,8 @@
}
/**
- * Stops the provider sending updates.
+ * Stops the provider sending further updates. This will be called after {@link
+ * #onStartUpdates(long)}.
*/
public abstract void onStopUpdates();
diff --git a/core/java/android/view/CrossWindowBlurListeners.java b/core/java/android/view/CrossWindowBlurListeners.java
index 5a1b850..55fc4f4 100644
--- a/core/java/android/view/CrossWindowBlurListeners.java
+++ b/core/java/android/view/CrossWindowBlurListeners.java
@@ -16,13 +16,19 @@
package android.view;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.util.ArraySet;
+import android.util.ArrayMap;
import android.util.Log;
+import com.android.internal.util.Preconditions;
+
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -42,7 +48,7 @@
private static final Object sLock = new Object();
private final BlurEnabledListenerInternal mListenerInternal = new BlurEnabledListenerInternal();
- private final ArraySet<Consumer<Boolean>> mListeners = new ArraySet();
+ private final ArrayMap<Consumer<Boolean>, Executor> mListeners = new ArrayMap();
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
private boolean mInternalListenerAttached = false;
private boolean mCrossWindowBlurEnabled;
@@ -74,20 +80,22 @@
}
}
- void addListener(Consumer<Boolean> listener) {
- if (listener == null) return;
+ void addListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<Boolean> listener) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ Preconditions.checkNotNull(executor, "executor cannot be null");
synchronized (sLock) {
attachInternalListenerIfNeededLocked();
- mListeners.add(listener);
- notifyListenerOnMain(listener, mCrossWindowBlurEnabled);
+ mListeners.put(listener, executor);
+ notifyListener(listener, executor, mCrossWindowBlurEnabled);
}
}
void removeListener(Consumer<Boolean> listener) {
- if (listener == null) return;
+ Preconditions.checkNotNull(listener, "listener cannot be null");
synchronized (sLock) {
mListeners.remove(listener);
@@ -116,10 +124,8 @@
}
}
- private void notifyListenerOnMain(Consumer<Boolean> listener, boolean enabled) {
- mMainHandler.post(() -> {
- listener.accept(enabled);
- });
+ private void notifyListener(Consumer<Boolean> listener, Executor executor, boolean enabled) {
+ executor.execute(() -> listener.accept(enabled));
}
private final class BlurEnabledListenerInternal extends ICrossWindowBlurEnabledListener.Stub {
@@ -128,8 +134,13 @@
synchronized (sLock) {
mCrossWindowBlurEnabled = enabled;
- for (int i = 0; i < mListeners.size(); i++) {
- notifyListenerOnMain(mListeners.valueAt(i), enabled);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ for (int i = 0; i < mListeners.size(); i++) {
+ notifyListener(mListeners.keyAt(i), mListeners.valueAt(i), enabled);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
}
diff --git a/core/java/android/view/DragAndDropPermissions.java b/core/java/android/view/DragAndDropPermissions.java
index d47604d..16204d8 100644
--- a/core/java/android/view/DragAndDropPermissions.java
+++ b/core/java/android/view/DragAndDropPermissions.java
@@ -16,12 +16,15 @@
package android.view;
+import static java.lang.Integer.toHexString;
+
import android.app.Activity;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
+import android.util.Log;
import com.android.internal.view.IDragAndDropPermissions;
@@ -56,9 +59,28 @@
*/
public final class DragAndDropPermissions implements Parcelable {
- private final IDragAndDropPermissions mDragAndDropPermissions;
+ private static final String TAG = "DragAndDrop";
+ private static final boolean DEBUG = false;
- private IBinder mTransientToken;
+ /**
+ * Permissions for a drop can be granted in one of two ways:
+ * <ol>
+ * <li>An app can explicitly request permissions using
+ * {@link Activity#requestDragAndDropPermissions(DragEvent)}. In this case permissions are
+ * revoked automatically when then activity is destroyed. See {@link #take(IBinder)}.
+ * <li>The platform can request permissions on behalf of the app (e.g. in
+ * {@link android.widget.Editor}). In this case permissions are revoked automatically when
+ * the app process terminates. See {@link #takeTransient()}.
+ * </ol>
+ *
+ * <p>In order to implement the second case above, we create a static token object here. This
+ * ensures that the token stays alive for the lifetime of the app process, allowing us to
+ * revoke permissions when the app process terminates using {@link IBinder#linkToDeath} in
+ * {@code DragAndDropPermissionsHandler}.
+ */
+ private static IBinder sAppToken;
+
+ private final IDragAndDropPermissions mDragAndDropPermissions;
/**
* Create a new {@link DragAndDropPermissions} object to control the access permissions for
@@ -81,30 +103,51 @@
}
/**
- * Take the permissions and bind their lifetime to the activity.
+ * Take permissions, binding their lifetime to the activity.
+ *
+ * <p>Note: This API is exposed to apps via
+ * {@link Activity#requestDragAndDropPermissions(DragEvent)}.
+ *
* @param activityToken Binder pointing to an Activity instance to bind the lifetime to.
* @return True if permissions are successfully taken.
+ *
* @hide
*/
public boolean take(IBinder activityToken) {
try {
+ if (DEBUG) {
+ Log.d(TAG, this + ": calling take() with activity-bound token: "
+ + toHexString(activityToken.hashCode()));
+ }
mDragAndDropPermissions.take(activityToken);
} catch (RemoteException e) {
+ Log.w(TAG, this + ": take() failed with a RemoteException", e);
return false;
}
return true;
}
/**
- * Take the permissions. Must call {@link #release} explicitly.
+ * Take permissions transiently. Permissions will be revoked when the app process terminates.
+ *
+ * <p>Note: This API is not exposed to apps.
+ *
* @return True if permissions are successfully taken.
+ *
* @hide
*/
public boolean takeTransient() {
try {
- mTransientToken = new Binder();
- mDragAndDropPermissions.takeTransient(mTransientToken);
+ if (sAppToken == null) {
+ sAppToken = new Binder();
+ }
+ if (DEBUG) {
+ Log.d(TAG, this + ": calling takeTransient() with process-bound token: "
+ + toHexString(sAppToken.hashCode()));
+ }
+ mDragAndDropPermissions.takeTransient(sAppToken);
} catch (RemoteException e) {
+ Log.w(TAG, this + ": takeTransient() failed with a RemoteException", e);
return false;
}
return true;
@@ -116,8 +159,8 @@
public void release() {
try {
mDragAndDropPermissions.release();
- mTransientToken = null;
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -142,11 +185,9 @@
@Override
public void writeToParcel(Parcel destination, int flags) {
destination.writeStrongInterface(mDragAndDropPermissions);
- destination.writeStrongBinder(mTransientToken);
}
private DragAndDropPermissions(Parcel in) {
mDragAndDropPermissions = IDragAndDropPermissions.Stub.asInterface(in.readStrongBinder());
- mTransientToken = in.readStrongBinder();
}
}
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
index ddb49786..10721ad 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -43,8 +43,11 @@
* accordingly. This should be called before `finish`
* @param taskId for which the leash should be updated
* @param destinationBounds bounds of the final PiP window
+ * @param windowCrop bounds to crop as part of final transform.
+ * @param float9 An array of 9 floats to be used as matrix transform.
*/
- void setFinishTaskBounds(int taskId, in Rect destinationBounds);
+ void setFinishTaskBounds(int taskId, in Rect destinationBounds, in Rect windowCrop,
+ in float[] float9);
/**
* Notifies to the system that the animation into Recents should end, and all leashes associated
diff --git a/core/java/android/view/RoundedCorner.java b/core/java/android/view/RoundedCorner.java
index cc7525b..56b4383 100644
--- a/core/java/android/view/RoundedCorner.java
+++ b/core/java/android/view/RoundedCorner.java
@@ -163,7 +163,7 @@
* @hide
*/
public boolean isEmpty() {
- return mRadius == 0 || mCenter.x == 0 || mCenter.y == 0;
+ return mRadius == 0 || mCenter.x <= 0 || mCenter.y <= 0;
}
private String getPositionString(@Position int position) {
diff --git a/core/java/android/view/RoundedCorners.java b/core/java/android/view/RoundedCorners.java
index 569c287..623d969 100644
--- a/core/java/android/view/RoundedCorners.java
+++ b/core/java/android/view/RoundedCorners.java
@@ -181,16 +181,16 @@
boolean hasRoundedCorner;
switch (position) {
case POSITION_TOP_LEFT:
- hasRoundedCorner = radius > insetTop || radius > insetLeft;
+ hasRoundedCorner = radius > insetTop && radius > insetLeft;
break;
case POSITION_TOP_RIGHT:
- hasRoundedCorner = radius > insetTop || radius > insetRight;
+ hasRoundedCorner = radius > insetTop && radius > insetRight;
break;
case POSITION_BOTTOM_RIGHT:
- hasRoundedCorner = radius > insetBottom || radius > insetRight;
+ hasRoundedCorner = radius > insetBottom && radius > insetRight;
break;
case POSITION_BOTTOM_LEFT:
- hasRoundedCorner = radius > insetBottom || radius > insetLeft;
+ hasRoundedCorner = radius > insetBottom && radius > insetLeft;
break;
default:
throw new IllegalArgumentException(
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 818a2b0..04512c9 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -81,6 +81,7 @@
import static android.view.WindowLayoutParamsProto.Y;
import android.Manifest.permission;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -121,6 +122,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -864,6 +866,33 @@
}
/**
+ * Adds a listener, which will be called when cross-window blurs are enabled/disabled at
+ * runtime. This affects both window blur behind (see {@link LayoutParams#setBlurBehindRadius})
+ * and window background blur (see {@link Window#setBackgroundBlurRadius}).
+ *
+ * Cross-window blur might not be supported by some devices due to GPU limitations. It can also
+ * be disabled at runtime, e.g. during battery saving mode, when multimedia tunneling is used or
+ * when minimal post processing is requested. In such situations, no blur will be computed or
+ * drawn, so the blur target area will not be blurred. To handle this, the app might want to
+ * change its theme to one that does not use blurs.
+ *
+ * If the listener is added successfully, it will be called immediately with the current
+ * cross-window blur enabled state.
+ *
+ * @param executor {@link Executor} to handle the listener callback
+ * @param listener the listener to be added. It will be called back with a boolean parameter,
+ * which is true if cross-window blur is enabled and false if it is disabled
+ *
+ * @see #removeCrossWindowBlurEnabledListener
+ * @see #isCrossWindowBlurEnabled
+ * @see LayoutParams#setBlurBehindRadius
+ * @see Window#setBackgroundBlurRadius
+ */
+ default void addCrossWindowBlurEnabledListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<Boolean> listener) {
+ }
+
+ /**
* Removes a listener, previously added with {@link #addCrossWindowBlurEnabledListener}
*
* @param listener the listener to be removed
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index e37522b..8dce852 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -23,6 +23,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UiContext;
@@ -40,6 +41,7 @@
import com.android.internal.os.IResultReceiver;
import java.util.List;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -310,7 +312,13 @@
@Override
public void addCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
- CrossWindowBlurListeners.getInstance().addListener(listener);
+ addCrossWindowBlurEnabledListener(mContext.getMainExecutor(), listener);
+ }
+
+ @Override
+ public void addCrossWindowBlurEnabledListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<Boolean> listener) {
+ CrossWindowBlurListeners.getInstance().addListener(executor, listener);
}
@Override
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 5d876a6..cc533eb 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -60,6 +60,7 @@
* @attr ref android.R.styleable#InputMethod_isDefault
* @attr ref android.R.styleable#InputMethod_supportsSwitchingToNextInputMethod
* @attr ref android.R.styleable#InputMethod_supportsInlineSuggestions
+ * @attr ref android.R.styleable#InputMethod_suppressesSpellChecker
*/
public final class InputMethodInfo implements Parcelable {
static final String TAG = "InputMethodInfo";
@@ -118,6 +119,11 @@
private final boolean mInlineSuggestionsEnabled;
/**
+ * The flag whether this IME suppresses spell checker.
+ */
+ private final boolean mSuppressesSpellChecker;
+
+ /**
* @param service the {@link ResolveInfo} corresponds in which the IME is implemented.
* @return a unique ID to be returned by {@link #getId()}. We have used
* {@link ComponentName#flattenToShortString()} for this purpose (and it is already
@@ -160,6 +166,7 @@
boolean isAuxIme = true;
boolean supportsSwitchingToNextInputMethod = false; // false as default
boolean inlineSuggestionsEnabled = false; // false as default
+ boolean suppressesSpellChecker = false; // false as default
mForceDefault = false;
PackageManager pm = context.getPackageManager();
@@ -203,6 +210,8 @@
false);
inlineSuggestionsEnabled = sa.getBoolean(
com.android.internal.R.styleable.InputMethod_supportsInlineSuggestions, false);
+ suppressesSpellChecker = sa.getBoolean(
+ com.android.internal.R.styleable.InputMethod_suppressesSpellChecker, false);
sa.recycle();
final int depth = parser.getDepth();
@@ -274,6 +283,7 @@
mIsAuxIme = isAuxIme;
mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
mInlineSuggestionsEnabled = inlineSuggestionsEnabled;
+ mSuppressesSpellChecker = suppressesSpellChecker;
mIsVrOnly = isVrOnly;
}
@@ -284,6 +294,7 @@
mIsAuxIme = source.readInt() == 1;
mSupportsSwitchingToNextInputMethod = source.readInt() == 1;
mInlineSuggestionsEnabled = source.readInt() == 1;
+ mSuppressesSpellChecker = source.readBoolean();
mIsVrOnly = source.readBoolean();
mService = ResolveInfo.CREATOR.createFromParcel(source);
mSubtypes = new InputMethodSubtypeArray(source);
@@ -342,6 +353,7 @@
mForceDefault = forceDefault;
mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
mInlineSuggestionsEnabled = inlineSuggestionsEnabled;
+ mSuppressesSpellChecker = false;
mIsVrOnly = isVrOnly;
}
@@ -494,7 +506,8 @@
+ " mSettingsActivityName=" + mSettingsActivityName
+ " mIsVrOnly=" + mIsVrOnly
+ " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod
- + " mInlineSuggestionsEnabled=" + mInlineSuggestionsEnabled);
+ + " mInlineSuggestionsEnabled=" + mInlineSuggestionsEnabled
+ + " mSuppressesSpellChecker=" + mSuppressesSpellChecker);
pw.println(prefix + "mIsDefaultResId=0x"
+ Integer.toHexString(mIsDefaultResId));
pw.println(prefix + "Service:");
@@ -563,6 +576,13 @@
}
/**
+ * Return {@code true} if this input method suppresses spell checker.
+ */
+ public boolean suppressesSpellChecker() {
+ return mSuppressesSpellChecker;
+ }
+
+ /**
* Used to package this object into a {@link Parcel}.
*
* @param dest The {@link Parcel} to be written.
@@ -576,6 +596,7 @@
dest.writeInt(mIsAuxIme ? 1 : 0);
dest.writeInt(mSupportsSwitchingToNextInputMethod ? 1 : 0);
dest.writeInt(mInlineSuggestionsEnabled ? 1 : 0);
+ dest.writeBoolean(mSuppressesSpellChecker);
dest.writeBoolean(mIsVrOnly);
mService.writeToParcel(dest, flags);
mSubtypes.writeToParcel(dest);
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 53bbc0a..ff4d671 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -444,6 +444,13 @@
*/
private Matrix mActivityViewToScreenMatrix = null;
+ /**
+ * As reported by {@link InputBindResult}. This value is determined by
+ * {@link com.android.internal.R.styleable#InputMethod_suppressesSpellChecking}.
+ */
+ @GuardedBy("mH")
+ private boolean mIsInputMethodSuppressingSpellChecker = false;
+
// -----------------------------------------------------------
/**
@@ -858,6 +865,8 @@
mCurId = res.id;
mBindSequence = res.sequence;
mActivityViewToScreenMatrix = res.getActivityViewToScreenMatrix();
+ mIsInputMethodSuppressingSpellChecker =
+ res.isInputMethodSuppressingSpellChecker;
}
startInputInner(StartInputReason.BOUND_TO_IMMS, null, 0, 0, 0);
return;
@@ -1299,7 +1308,9 @@
* @return {@link List} of {@link InputMethodInfo}.
* @hide
*/
+ @TestApi
@RequiresPermission(INTERACT_ACROSS_USERS_FULL)
+ @NonNull
public List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
try {
final Completable.InputMethodInfoList value = Completable.createInputMethodInfoList();
@@ -1470,6 +1481,15 @@
}
/**
+ * Return {@code true} if the input method is suppressing system spell checker.
+ */
+ public boolean isInputMethodSuppressingSpellChecker() {
+ synchronized (mH) {
+ return mIsInputMethodSuppressingSpellChecker;
+ }
+ }
+
+ /**
* Reset all of the state associated with being bound to an input method.
*/
void clearBindingLocked() {
@@ -1513,6 +1533,7 @@
@UnsupportedAppUsage
void finishInputLocked() {
mActivityViewToScreenMatrix = null;
+ mIsInputMethodSuppressingSpellChecker = false;
setNextServedViewLocked(null);
if (getServedViewLocked() != null) {
if (DEBUG) {
@@ -2037,6 +2058,7 @@
return false;
}
mActivityViewToScreenMatrix = res.getActivityViewToScreenMatrix();
+ mIsInputMethodSuppressingSpellChecker = res.isInputMethodSuppressingSpellChecker;
if (res.id != null) {
setInputChannelLocked(res.channel);
mBindSequence = res.sequence;
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 6d5077a..ef50045 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -145,7 +145,7 @@
@StringDef({WIDGET_TYPE_TEXTVIEW, WIDGET_TYPE_EDITTEXT, WIDGET_TYPE_UNSELECTABLE_TEXTVIEW,
WIDGET_TYPE_WEBVIEW, WIDGET_TYPE_EDIT_WEBVIEW, WIDGET_TYPE_CUSTOM_TEXTVIEW,
WIDGET_TYPE_CUSTOM_EDITTEXT, WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW,
- WIDGET_TYPE_NOTIFICATION, WIDGET_TYPE_UNKNOWN})
+ WIDGET_TYPE_NOTIFICATION, WIDGET_TYPE_CLIPBOARD, WIDGET_TYPE_UNKNOWN })
@interface WidgetType {}
/** The widget involved in the text classification context is a standard
@@ -172,6 +172,8 @@
String WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
/** The widget involved in the text classification context is a notification */
String WIDGET_TYPE_NOTIFICATION = "notification";
+ /** The text classification context is for use with the system clipboard. */
+ String WIDGET_TYPE_CLIPBOARD = "clipboard";
/** The widget involved in the text classification context is of an unknown/unspecified type. */
String WIDGET_TYPE_UNKNOWN = "unknown";
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index 35d8445..ba58b65 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -25,6 +25,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
+import android.view.inputmethod.InputMethodManager;
import com.android.internal.textservice.ISpellCheckerSession;
import com.android.internal.textservice.ISpellCheckerSessionListener;
@@ -176,6 +177,11 @@
* @param suggestionsLimit the maximum number of suggestions that will be returned
*/
public void getSentenceSuggestions(TextInfo[] textInfos, int suggestionsLimit) {
+ final InputMethodManager imm = mTextServicesManager.getInputMethodManager();
+ if (imm != null && imm.isInputMethodSuppressingSpellChecker()) {
+ handleOnGetSentenceSuggestionsMultiple(new SentenceSuggestionsInfo[0]);
+ return;
+ }
mSpellCheckerSessionListenerImpl.getSentenceSuggestionsMultiple(
textInfos, suggestionsLimit);
}
@@ -204,6 +210,11 @@
if (DBG) {
Log.w(TAG, "getSuggestions from " + mSpellCheckerInfo.getId());
}
+ final InputMethodManager imm = mTextServicesManager.getInputMethodManager();
+ if (imm != null && imm.isInputMethodSuppressingSpellChecker()) {
+ handleOnGetSuggestionsMultiple(new SuggestionsInfo[0]);
+ return;
+ }
mSpellCheckerSessionListenerImpl.getSuggestionsMultiple(
textInfos, suggestionsLimit, sequentialWords);
}
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index 996757d..6fb01a3 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -30,6 +30,7 @@
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.UserHandle;
import android.util.Log;
+import android.view.inputmethod.InputMethodManager;
import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener;
import com.android.internal.textservice.ISpellCheckerSessionListener;
@@ -88,10 +89,15 @@
@UserIdInt
private final int mUserId;
- private TextServicesManager(@UserIdInt int userId) throws ServiceNotFoundException {
+ @Nullable
+ private final InputMethodManager mInputMethodManager;
+
+ private TextServicesManager(@UserIdInt int userId,
+ @Nullable InputMethodManager inputMethodManager) throws ServiceNotFoundException {
mService = ITextServicesManager.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.TEXT_SERVICES_MANAGER_SERVICE));
mUserId = userId;
+ mInputMethodManager = inputMethodManager;
}
/**
@@ -105,7 +111,8 @@
@NonNull
public static TextServicesManager createInstance(@NonNull Context context)
throws ServiceNotFoundException {
- return new TextServicesManager(context.getUserId());
+ return new TextServicesManager(context.getUserId(), context.getSystemService(
+ InputMethodManager.class));
}
/**
@@ -118,7 +125,7 @@
synchronized (TextServicesManager.class) {
if (sInstance == null) {
try {
- sInstance = new TextServicesManager(UserHandle.myUserId());
+ sInstance = new TextServicesManager(UserHandle.myUserId(), null);
} catch (ServiceNotFoundException e) {
throw new IllegalStateException(e);
}
@@ -127,6 +134,12 @@
}
}
+ /** @hide */
+ @Nullable
+ public InputMethodManager getInputMethodManager() {
+ return mInputMethodManager;
+ }
+
/**
* Returns the language component of a given locale string.
*/
diff --git a/core/java/android/view/translation/ITranslationManager.aidl b/core/java/android/view/translation/ITranslationManager.aidl
index 7f6c4b4..d347f31 100644
--- a/core/java/android/view/translation/ITranslationManager.aidl
+++ b/core/java/android/view/translation/ITranslationManager.aidl
@@ -17,6 +17,7 @@
package android.view.translation;
import android.os.IBinder;
+import android.os.IRemoteCallback;
import android.view.autofill.AutofillId;
import android.view.translation.TranslationSpec;
import com.android.internal.os.IResultReceiver;
@@ -40,4 +41,7 @@
void updateUiTranslationStateByTaskId(int state, in TranslationSpec sourceSpec,
in TranslationSpec destSpec, in List<AutofillId> viewIds, int taskId,
int userId);
+
+ void registerUiTranslationStateCallback(in IRemoteCallback callback, int userId);
+ void unregisterUiTranslationStateCallback(in IRemoteCallback callback, int userId);
}
diff --git a/core/java/android/view/translation/UiTranslationManager.java b/core/java/android/view/translation/UiTranslationManager.java
index 7c73e70..9fba95f 100644
--- a/core/java/android/view/translation/UiTranslationManager.java
+++ b/core/java/android/view/translation/UiTranslationManager.java
@@ -16,28 +16,36 @@
package android.view.translation;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.app.assist.ActivityId;
import android.content.Context;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IRemoteCallback;
import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
import android.view.View;
import android.view.autofill.AutofillId;
+import com.android.internal.annotations.GuardedBy;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.Executor;
+// TODO(b/178044703): Describe what UI Translation is.
/**
* The {@link UiTranslationManager} class provides ways for apps to use the ui translation
* function in framework.
- *
- * @hide
*/
-@SystemApi
public final class UiTranslationManager {
private static final String TAG = "UiTranslationManager";
@@ -88,6 +96,14 @@
public @interface UiTranslationState {
}
+ // Keys for the data transmitted in the internal UI Translation state callback.
+ /** @hide */
+ public static final String EXTRA_STATE = "state";
+ /** @hide */
+ public static final String EXTRA_SOURCE_LOCALE = "source_locale";
+ /** @hide */
+ public static final String EXTRA_TARGET_LOCALE = "target_locale";
+
@NonNull
private final Context mContext;
@@ -111,9 +127,12 @@
* @param destSpec {@link TranslationSpec} for the translated data.
* @param viewIds A list of the {@link View}'s {@link AutofillId} which needs to be translated
* @param taskId the Activity Task id which needs ui translation
+ *
+ * @hide
*/
// TODO, hide the APIs
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void startTranslation(@NonNull TranslationSpec sourceSpec,
@NonNull TranslationSpec destSpec, @NonNull List<AutofillId> viewIds,
int taskId) {
@@ -141,8 +160,11 @@
* @throws IllegalArgumentException if the no {@link View}'s {@link AutofillId} in the list
* @throws NullPointerException the sourceSpec, destSpec, viewIds, activityId or
* {@link android.app.assist.ActivityId#getToken()} is {@code null}
+ *
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void startTranslation(@NonNull TranslationSpec sourceSpec,
@NonNull TranslationSpec destSpec, @NonNull List<AutofillId> viewIds,
@NonNull ActivityId activityId) {
@@ -171,9 +193,12 @@
* NOTE: Please use {@code finishTranslation(ActivityId)} instead.
*
* @param taskId the Activity Task id which needs ui translation
+ *
+ * @hide
*/
// TODO, hide the APIs
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void finishTranslation(int taskId) {
try {
mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_FINISHED,
@@ -191,8 +216,11 @@
* @param activityId the identifier for the Activity which needs ui translation
* @throws NullPointerException the activityId or
* {@link android.app.assist.ActivityId#getToken()} is {@code null}
+ *
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void finishTranslation(@NonNull ActivityId activityId) {
try {
Objects.requireNonNull(activityId);
@@ -212,9 +240,12 @@
* NOTE: Please use {@code pauseTranslation(ActivityId)} instead.
*
* @param taskId the Activity Task id which needs ui translation
+ *
+ * @hide
*/
// TODO, hide the APIs
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void pauseTranslation(int taskId) {
try {
mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_PAUSED,
@@ -232,8 +263,11 @@
* @param activityId the identifier for the Activity which needs ui translation
* @throws NullPointerException the activityId or
* {@link android.app.assist.ActivityId#getToken()} is {@code null}
+ *
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void pauseTranslation(@NonNull ActivityId activityId) {
try {
Objects.requireNonNull(activityId);
@@ -253,9 +287,12 @@
* NOTE: Please use {@code resumeTranslation(ActivityId)} instead.
*
* @param taskId the Activity Task id which needs ui translation
+ *
+ * @hide
*/
// TODO, hide the APIs
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void resumeTranslation(int taskId) {
try {
mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_RESUMED,
@@ -273,8 +310,11 @@
* @param activityId the identifier for the Activity which needs ui translation
* @throws NullPointerException the activityId or
* {@link android.app.assist.ActivityId#getToken()} is {@code null}
+ *
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void resumeTranslation(@NonNull ActivityId activityId) {
try {
Objects.requireNonNull(activityId);
@@ -286,4 +326,104 @@
throw e.rethrowFromSystemServer();
}
}
+
+ // TODO(b/178044703): Fix the View API link when it becomes public.
+ /**
+ * Register for notifications of UI Translation state changes on the foreground activity. This
+ * is available to the owning application itself and also the current input method.
+ * <p>
+ * The application whose UI is being translated can use this to customize the UI Translation
+ * behavior in ways that aren't made easy by methods like
+ * View#onCreateTranslationRequest().
+ * <p>
+ * Input methods can use this to offer complementary features to UI Translation; for example,
+ * enabling outgoing message translation when the system is translating incoming messages in a
+ * communication app.
+ *
+ * @param callback the callback to register for receiving the state change
+ * notifications
+ */
+ public void registerUiTranslationStateCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull UiTranslationStateCallback callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+ synchronized (mCallbacks) {
+ if (mCallbacks.containsKey(callback)) {
+ Log.w(TAG, "registerUiTranslationStateCallback: callback already registered;"
+ + " ignoring.");
+ return;
+ }
+ final IRemoteCallback remoteCallback =
+ new UiTranslationStateRemoteCallback(executor, callback);
+ try {
+ mService.registerUiTranslationStateCallback(remoteCallback, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ mCallbacks.put(callback, remoteCallback);
+ }
+ }
+
+ /**
+ * Unregister {@code callback}.
+ *
+ * @see #registerUiTranslationStateCallback(Executor, UiTranslationStateCallback)
+ */
+ public void unregisterUiTranslationStateCallback(@NonNull UiTranslationStateCallback callback) {
+ Objects.requireNonNull(callback);
+
+ synchronized (mCallbacks) {
+ final IRemoteCallback remoteCallback = mCallbacks.get(callback);
+ if (remoteCallback == null) {
+ Log.w(TAG, "unregisterUiTranslationStateCallback: callback not found; ignoring.");
+ return;
+ }
+ try {
+ mService.unregisterUiTranslationStateCallback(remoteCallback, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ mCallbacks.remove(callback);
+ }
+ }
+
+ @NonNull
+ @GuardedBy("mCallbacks")
+ private final Map<UiTranslationStateCallback, IRemoteCallback> mCallbacks = new ArrayMap<>();
+
+ private static class UiTranslationStateRemoteCallback extends IRemoteCallback.Stub {
+ private final Executor mExecutor;
+ private final UiTranslationStateCallback mCallback;
+
+ UiTranslationStateRemoteCallback(Executor executor,
+ UiTranslationStateCallback callback) {
+ mExecutor = executor;
+ mCallback = callback;
+ }
+
+ @Override
+ public void sendResult(Bundle bundle) {
+ Binder.clearCallingIdentity();
+ mExecutor.execute(() -> {
+ int state = bundle.getInt(EXTRA_STATE);
+ switch (state) {
+ case STATE_UI_TRANSLATION_STARTED:
+ case STATE_UI_TRANSLATION_RESUMED:
+ mCallback.onStarted(
+ bundle.getString(EXTRA_SOURCE_LOCALE),
+ bundle.getString(EXTRA_TARGET_LOCALE));
+ break;
+ case STATE_UI_TRANSLATION_PAUSED:
+ mCallback.onPaused();
+ break;
+ case STATE_UI_TRANSLATION_FINISHED:
+ mCallback.onFinished();
+ break;
+ default:
+ Log.wtf(TAG, "Unexpected translation state:" + state);
+ }
+ });
+ }
+ }
}
diff --git a/core/java/android/view/translation/UiTranslationStateCallback.java b/core/java/android/view/translation/UiTranslationStateCallback.java
new file mode 100644
index 0000000..1946b70
--- /dev/null
+++ b/core/java/android/view/translation/UiTranslationStateCallback.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.translation;
+
+import android.annotation.NonNull;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Callback for listening to UI Translation state changes. See {@link
+ * UiTranslationManager#registerUiTranslationStateCallback(Executor, UiTranslationStateCallback)}.
+ */
+public interface UiTranslationStateCallback {
+
+ /**
+ * The system is requesting translation of the UI from {@code sourceLocale} to {@code
+ * targetLocale}.
+ * <p>
+ * This is also called if either the requested {@code sourceLocale} or {@code targetLocale} has
+ * changed; or called again after {@link #onPaused()}.
+ */
+ void onStarted(@NonNull String sourceLocale, @NonNull String targetLocale);
+
+ /**
+ * The system is requesting that the application temporarily show the UI contents in their
+ * original language.
+ */
+ void onPaused();
+
+ /**
+ * The UI Translation session has ended.
+ */
+ void onFinished();
+}
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index 45352e4..c203c790 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -130,7 +130,7 @@
public @interface EdgeEffectType {
}
- private static final float DEFAULT_MAX_STRETCH_INTENSITY = 1.5f;
+ private static final float DEFAULT_MAX_STRETCH_INTENSITY = 0.08f;
@SuppressWarnings("UnusedDeclaration")
private static final String TAG = "EdgeEffect";
@@ -177,6 +177,7 @@
private long mStartTime;
private float mDuration;
private float mStretchIntensity = DEFAULT_MAX_STRETCH_INTENSITY;
+ private float mStretchDistanceFraction = 0.1f;
private float mStretchDistance = -1f;
private final Interpolator mInterpolator = new DecelerateInterpolator();
@@ -467,7 +468,7 @@
public void onAbsorb(int velocity) {
if (mEdgeEffectType == TYPE_STRETCH) {
mState = STATE_RECEDE;
- mVelocity = velocity / mHeight;
+ mVelocity = velocity;
mDistance = 0;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
} else {
@@ -655,7 +656,7 @@
// for now leverage placeholder logic if no stretch distance is provided to
// consume the displacement ratio times the minimum of the width or height
mStretchDistance > 0 ? mStretchDistance :
- (mDisplacement * Math.min(mWidth, mHeight))
+ (mStretchDistanceFraction * Math.min(mWidth, mHeight))
);
}
@@ -745,9 +746,9 @@
final double mDampedFreq = NATURAL_FREQUENCY * Math.sqrt(1 - DAMPING_RATIO * DAMPING_RATIO);
// We're always underdamped, so we can use only those equations:
- double cosCoeff = mDistance;
+ double cosCoeff = mDistance * mHeight;
double sinCoeff = (1 / mDampedFreq) * (DAMPING_RATIO * NATURAL_FREQUENCY
- * mDistance + mVelocity);
+ * mDistance * mHeight + mVelocity);
double distance = Math.pow(Math.E, -DAMPING_RATIO * NATURAL_FREQUENCY * deltaT)
* (cosCoeff * Math.cos(mDampedFreq * deltaT)
+ sinCoeff * Math.sin(mDampedFreq * deltaT));
@@ -755,7 +756,7 @@
+ Math.pow(Math.E, -DAMPING_RATIO * NATURAL_FREQUENCY * deltaT)
* (-mDampedFreq * cosCoeff * Math.sin(mDampedFreq * deltaT)
+ mDampedFreq * sinCoeff * Math.cos(mDampedFreq * deltaT));
- mDistance = (float) distance;
+ mDistance = (float) distance / mHeight;
mVelocity = (float) velocity;
mStartTime = time;
if (isAtEquilibrium()) {
@@ -786,9 +787,8 @@
* considered at rest or false if it is still animating.
*/
private boolean isAtEquilibrium() {
- double velocity = mVelocity * mHeight; // in pixels/second
double displacement = mDistance * mHeight; // in pixels
- return Math.abs(velocity) < VELOCITY_THRESHOLD
+ return Math.abs(mVelocity) < VELOCITY_THRESHOLD
&& Math.abs(displacement) < VALUE_THRESHOLD;
}
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 7517b80..238ce85 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -988,6 +988,12 @@
if (mTextView.isTextEditable() && mTextView.isSuggestionsEnabled()
&& !(mTextView.isInExtractedMode())) {
+ final InputMethodManager imm = getInputMethodManager();
+ if (imm != null && imm.isInputMethodSuppressingSpellChecker()) {
+ // Do not close mSpellChecker here as it may be reused when the current IME has been
+ // changed.
+ return;
+ }
if (mSpellChecker == null && createSpellChecker) {
mSpellChecker = new SpellChecker(mTextView);
}
@@ -2898,9 +2904,6 @@
} finally {
mTextView.endBatchEdit();
mUndoInputFilter.freezeLastEdit();
- if (permissions != null) {
- permissions.release();
- }
}
}
diff --git a/core/java/android/widget/RemoteCollectionItemsAdapter.java b/core/java/android/widget/RemoteCollectionItemsAdapter.java
new file mode 100644
index 0000000..d843308
--- /dev/null
+++ b/core/java/android/widget/RemoteCollectionItemsAdapter.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.widget;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.SparseIntArray;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RemoteViews.ColorResources;
+import android.widget.RemoteViews.InteractionHandler;
+import android.widget.RemoteViews.RemoteCollectionItems;
+
+import com.android.internal.R;
+
+import java.util.stream.IntStream;
+
+/**
+ * List {@link Adapter} backed by a {@link RemoteCollectionItems}.
+ *
+ * @hide
+ */
+class RemoteCollectionItemsAdapter extends BaseAdapter {
+
+ private final int mViewTypeCount;
+
+ private RemoteCollectionItems mItems;
+ private InteractionHandler mInteractionHandler;
+ private ColorResources mColorResources;
+
+ private SparseIntArray mLayoutIdToViewType;
+
+ RemoteCollectionItemsAdapter(
+ @NonNull RemoteCollectionItems items,
+ @NonNull InteractionHandler interactionHandler,
+ @NonNull ColorResources colorResources) {
+ // View type count can never increase after an adapter has been set on a ListView.
+ // Additionally, decreasing it could inhibit view recycling if the count were to back and
+ // forth between 3-2-3-2 for example. Therefore, the view type count, should be fixed for
+ // the lifetime of the adapter.
+ mViewTypeCount = items.getViewTypeCount();
+
+ mItems = items;
+ mInteractionHandler = interactionHandler;
+ mColorResources = colorResources;
+
+ initLayoutIdToViewType();
+ }
+
+ /**
+ * Updates the data for the adapter, allowing recycling of views. Note that if the view type
+ * count has increased, a new adapter should be created and set on the AdapterView instead of
+ * calling this method.
+ */
+ void setData(
+ @NonNull RemoteCollectionItems items,
+ @NonNull InteractionHandler interactionHandler,
+ @NonNull ColorResources colorResources) {
+ if (mViewTypeCount < items.getViewTypeCount()) {
+ throw new IllegalArgumentException(
+ "RemoteCollectionItemsAdapter cannot increase view type count after creation");
+ }
+
+ mItems = items;
+ mInteractionHandler = interactionHandler;
+ mColorResources = colorResources;
+
+ initLayoutIdToViewType();
+
+ notifyDataSetChanged();
+ }
+
+ private void initLayoutIdToViewType() {
+ SparseIntArray previousLayoutIdToViewType = mLayoutIdToViewType;
+ mLayoutIdToViewType = new SparseIntArray(mViewTypeCount);
+
+ int[] layoutIds = IntStream.range(0, mItems.getItemCount())
+ .map(position -> mItems.getItemView(position).getLayoutId())
+ .distinct()
+ .toArray();
+ if (layoutIds.length > mViewTypeCount) {
+ throw new IllegalArgumentException(
+ "Collection items uses " + layoutIds.length + " distinct layouts, which is "
+ + "more than view type count of " + mViewTypeCount);
+ }
+
+ // Tracks whether a layout id (by index, not value) has been assigned a view type.
+ boolean[] processedLayoutIdIndices = new boolean[layoutIds.length];
+ // Tracks whether a view type has been assigned to a layout id already.
+ boolean[] assignedViewTypes = new boolean[mViewTypeCount];
+
+ if (previousLayoutIdToViewType != null) {
+ for (int i = 0; i < layoutIds.length; i++) {
+ int layoutId = layoutIds[i];
+ // Copy over any previously used view types for layout ids in the collection to keep
+ // view types stable across data updates.
+ int previousViewType = previousLayoutIdToViewType.get(layoutId, -1);
+ // Skip this layout id if it wasn't assigned to a view type previously.
+ if (previousViewType < 0) continue;
+
+ mLayoutIdToViewType.put(layoutId, previousViewType);
+ processedLayoutIdIndices[i] = true;
+ assignedViewTypes[previousViewType] = true;
+ }
+ }
+
+ int lastViewType = -1;
+ for (int i = 0; i < layoutIds.length; i++) {
+ // If a view type has already been assigned to the layout id, skip it.
+ if (processedLayoutIdIndices[i]) continue;
+
+ int layoutId = layoutIds[i];
+ // If no view type is assigned for the layout id, choose the next possible value that
+ // isn't already assigned to a layout id. There is guaranteed to be some value available
+ // due to the prior validation logic that count(distinct layout ids) <= viewTypeCount.
+ int viewType = IntStream.range(lastViewType + 1, layoutIds.length)
+ .filter(type -> !assignedViewTypes[type])
+ .findFirst()
+ .orElseThrow(
+ () -> new IllegalStateException(
+ "RemoteCollectionItems has more distinct layout ids than its "
+ + "view type count"));
+ mLayoutIdToViewType.put(layoutId, viewType);
+ processedLayoutIdIndices[i] = true;
+ assignedViewTypes[viewType] = true;
+ lastViewType = viewType;
+ }
+ }
+
+ @Override
+ public int getCount() {
+ return mItems.getItemCount();
+ }
+
+ @Override
+ public RemoteViews getItem(int position) {
+ return mItems.getItemView(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return mItems.getItemId(position);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return mLayoutIdToViewType.get(mItems.getItemView(position).getLayoutId());
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return mViewTypeCount;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return mItems.hasStableIds();
+ }
+
+ @Nullable
+ @Override
+ public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
+ if (position >= getCount()) return null;
+
+ RemoteViews item = mItems.getItemView(position);
+ item.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
+ View reapplyView = getViewToReapply(convertView, item);
+
+ // Reapply the RemoteViews if we can.
+ if (reapplyView != null) {
+ try {
+ item.reapply(
+ parent.getContext(),
+ reapplyView,
+ mInteractionHandler,
+ null /* size */,
+ mColorResources);
+ return reapplyView;
+ } catch (RuntimeException e) {
+ // We can't reapply for some reason, we'll fallback to an apply and inflate a
+ // new view.
+ }
+ }
+
+ return item.apply(
+ parent.getContext(),
+ parent,
+ mInteractionHandler,
+ null /* size */,
+ mColorResources);
+ }
+
+ /** Returns {@code convertView} if it can be used to reapply {@code item}, or null otherwise. */
+ @Nullable
+ private static View getViewToReapply(@Nullable View convertView, @NonNull RemoteViews item) {
+ if (convertView == null) return null;
+
+ Object layoutIdTag = convertView.getTag(R.id.widget_frame);
+ if (!(layoutIdTag instanceof Integer)) return null;
+
+ return item.getLayoutId() == (Integer) layoutIdTag ? convertView : null;
+ }
+}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index d2f4cea..d02dd8c 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -28,6 +28,7 @@
import android.annotation.Px;
import android.annotation.StringRes;
import android.annotation.StyleRes;
+import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ActivityOptions;
import android.app.ActivityThread;
@@ -74,6 +75,7 @@
import android.util.DisplayMetrics;
import android.util.IntArray;
import android.util.Log;
+import android.util.LongArray;
import android.util.Pair;
import android.util.SizeF;
import android.util.SparseIntArray;
@@ -112,6 +114,7 @@
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -224,6 +227,7 @@
private static final int SET_VIEW_OUTLINE_RADIUS_TAG = 28;
private static final int SET_ON_CHECKED_CHANGE_RESPONSE_TAG = 29;
private static final int NIGHT_MODE_REFLECTION_ACTION_TAG = 30;
+ private static final int SET_REMOTE_COLLECTION_ITEMS_ADAPTER_TAG = 31;
/** @hide **/
@IntDef(prefix = "MARGIN_", value = {
@@ -899,6 +903,72 @@
ArrayList<RemoteViews> list;
}
+ private static class SetRemoteCollectionItemListAdapterAction extends Action {
+ private final RemoteCollectionItems mItems;
+
+ SetRemoteCollectionItemListAdapterAction(@IdRes int id, RemoteCollectionItems items) {
+ viewId = id;
+ mItems = items;
+ }
+
+ SetRemoteCollectionItemListAdapterAction(Parcel parcel) {
+ viewId = parcel.readInt();
+ mItems = parcel.readTypedObject(RemoteCollectionItems.CREATOR);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(viewId);
+ dest.writeTypedObject(mItems, flags);
+ }
+
+ @Override
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) throws ActionException {
+ View target = root.findViewById(viewId);
+ if (target == null) return;
+
+ if (!(target instanceof AdapterView)) {
+ Log.e(LOG_TAG, "Cannot call setRemoteAdapter on a view which is not "
+ + "an AdapterView (id: " + viewId + ")");
+ return;
+ }
+
+ AdapterView adapterView = (AdapterView) target;
+ Adapter adapter = adapterView.getAdapter();
+ // We can reuse the adapter if it's a RemoteCollectionItemsAdapter and the view type
+ // count hasn't increased. Note that AbsListView allocates a fixed size array for view
+ // recycling in setAdapter, so we must call setAdapter again if the number of view types
+ // increases.
+ if (adapter instanceof RemoteCollectionItemsAdapter
+ && adapter.getViewTypeCount() >= mItems.getViewTypeCount()) {
+ try {
+ ((RemoteCollectionItemsAdapter) adapter).setData(
+ mItems, handler, colorResources);
+ } catch (Throwable throwable) {
+ // setData should never failed with the validation in the items builder, but if
+ // it does, catch and rethrow.
+ throw new ActionException(throwable);
+ }
+ return;
+ }
+
+ try {
+ adapterView.setAdapter(
+ new RemoteCollectionItemsAdapter(mItems, handler, colorResources));
+ } catch (Throwable throwable) {
+ // This could throw if the AdapterView somehow doesn't accept BaseAdapter due to
+ // a type error.
+ throw new ActionException(throwable);
+ }
+ }
+
+ @Override
+ public int getActionTag() {
+ return SET_REMOTE_COLLECTION_ITEMS_ADAPTER_TAG;
+ }
+ }
+
private class SetRemoteViewsAdapterIntent extends Action {
public SetRemoteViewsAdapterIntent(@IdRes int id, Intent intent) {
this.viewId = id;
@@ -3543,6 +3613,8 @@
return new SetOnCheckedChangeResponse(parcel);
case NIGHT_MODE_REFLECTION_ACTION_TAG:
return new NightModeReflectionAction(parcel);
+ case SET_REMOTE_COLLECTION_ITEMS_ADAPTER_TAG:
+ return new SetRemoteCollectionItemListAdapterAction(parcel);
default:
throw new ActionException("Tag " + tag + " not found");
}
@@ -4215,6 +4287,25 @@
}
/**
+ * Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView,
+ * ie. {@link ListView}, {@link GridView}, {@link StackView} or {@link AdapterViewAnimator}.
+ * This is a simpler but less flexible approach to populating collection widgets. Its use is
+ * encouraged for most scenarios, as long as the total memory within the list of RemoteViews
+ * is relatively small (ie. doesn't contain large or numerous Bitmaps, see {@link
+ * RemoteViews#setImageViewBitmap}). In the case of numerous images, the use of API is still
+ * possible by setting image URIs instead of Bitmaps, see {@link RemoteViews#setImageViewUri}.
+ *
+ * This API is supported in the compatibility library for previous API levels, see
+ * RemoteViewsCompat.
+ *
+ * @param viewId The id of the {@link AdapterView}.
+ * @param items The items to display in the {@link AdapterView}.
+ */
+ public void setRemoteAdapter(@IdRes int viewId, @NonNull RemoteCollectionItems items) {
+ addAction(new SetRemoteCollectionItemListAdapterAction(viewId, items));
+ }
+
+ /**
* Equivalent to calling {@link ListView#smoothScrollToPosition(int)}.
*
* @param viewId The id of the view to change
@@ -4335,8 +4426,7 @@
/**
* Sets an OutlineProvider on the view whose corner radius is a dimension calculated using
- * {@link TypedValue#applyDimension(int, float, DisplayMetrics)}. This outline may change shape
- * during system transitions.
+ * {@link TypedValue#applyDimension(int, float, DisplayMetrics)}.
*
* <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0.
* Setting margins in pixels will behave poorly when the RemoteViews object is used on a
@@ -4349,7 +4439,7 @@
/**
* Sets an OutlineProvider on the view whose corner radius is a dimension resource with
- * {@code resId}. This outline may change shape during system transitions.
+ * {@code resId}.
*/
public void setViewOutlinePreferredRadiusDimen(@IdRes int viewId, @DimenRes int resId) {
addAction(new SetViewOutlinePreferredRadiusAction(viewId, resId));
@@ -6026,6 +6116,203 @@
return true;
}
+ /** Representation of a fixed list of items to be displayed in a RemoteViews collection. */
+ public static final class RemoteCollectionItems implements Parcelable {
+ private final long[] mIds;
+ private final RemoteViews[] mViews;
+ private final boolean mHasStableIds;
+ private final int mViewTypeCount;
+
+ RemoteCollectionItems(
+ long[] ids, RemoteViews[] views, boolean hasStableIds, int viewTypeCount) {
+ mIds = ids;
+ mViews = views;
+ mHasStableIds = hasStableIds;
+ mViewTypeCount = viewTypeCount;
+ if (ids.length != views.length) {
+ throw new IllegalArgumentException(
+ "RemoteCollectionItems has different number of ids and views");
+ }
+ if (viewTypeCount < 1) {
+ throw new IllegalArgumentException("View type count must be >= 1");
+ }
+ int layoutIdCount = (int) Arrays.stream(views)
+ .mapToInt(RemoteViews::getLayoutId)
+ .distinct()
+ .count();
+ if (layoutIdCount > viewTypeCount) {
+ throw new IllegalArgumentException(
+ "View type count is set to " + viewTypeCount + ", but the collection "
+ + "contains " + layoutIdCount + " different layout ids");
+ }
+ }
+
+ RemoteCollectionItems(Parcel in) {
+ int length = in.readInt();
+ mIds = new long[length];
+ in.readLongArray(mIds);
+ mViews = new RemoteViews[length];
+ in.readTypedArray(mViews, RemoteViews.CREATOR);
+ mHasStableIds = in.readBoolean();
+ mViewTypeCount = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mIds.length);
+ dest.writeLongArray(mIds);
+ dest.writeTypedArray(mViews, flags);
+ dest.writeBoolean(mHasStableIds);
+ dest.writeInt(mViewTypeCount);
+ }
+
+ /**
+ * Returns the id for {@code position}. See {@link #hasStableIds()} for whether this id
+ * should be considered meaningful across collection updates.
+ *
+ * @return Id for the position.
+ */
+ public long getItemId(int position) {
+ return mIds[position];
+ }
+
+ /**
+ * Returns the {@link RemoteViews} to display at {@code position}.
+ *
+ * @return RemoteViews for the position.
+ */
+ @NonNull
+ public RemoteViews getItemView(int position) {
+ return mViews[position];
+ }
+
+ /**
+ * Returns the number of elements in the collection.
+ *
+ * @return Count of items.
+ */
+ public int getItemCount() {
+ return mIds.length;
+ }
+
+ /**
+ * Returns the view type count for the collection when used in an adapter
+ *
+ * @return Count of view types for the collection when used in an adapter.
+ * @see android.widget.Adapter#getViewTypeCount()
+ */
+ public int getViewTypeCount() {
+ return mViewTypeCount;
+ }
+
+ /**
+ * Indicates whether the item ids are stable across changes to the underlying data.
+ *
+ * @return True if the same id always refers to the same object.
+ * @see android.widget.Adapter#hasStableIds()
+ */
+ public boolean hasStableIds() {
+ return mHasStableIds;
+ }
+
+ @NonNull
+ public static final Creator<RemoteCollectionItems> CREATOR =
+ new Creator<RemoteCollectionItems>() {
+ @NonNull
+ @Override
+ public RemoteCollectionItems createFromParcel(@NonNull Parcel source) {
+ return new RemoteCollectionItems(source);
+ }
+
+ @NonNull
+ @Override
+ public RemoteCollectionItems[] newArray(int size) {
+ return new RemoteCollectionItems[size];
+ }
+ };
+
+ /** Builder class for {@link RemoteCollectionItems} objects.*/
+ public static final class Builder {
+ private final LongArray mIds = new LongArray();
+ private final List<RemoteViews> mViews = new ArrayList<>();
+ private boolean mHasStableIds;
+ private int mViewTypeCount;
+
+ /**
+ * Adds a {@link RemoteViews} to the collection.
+ *
+ * @param id Id to associate with the row. Use {@link #setHasStableIds(boolean)} to
+ * indicate that ids are stable across changes to the collection.
+ * @param view RemoteViews to display for the row.
+ */
+ @NonNull
+ // Covered by getItemId, getItemView, getItemCount.
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder addItem(long id, @NonNull RemoteViews view) {
+ if (view == null) throw new NullPointerException();
+ if (view.hasMultipleLayouts()) {
+ throw new IllegalArgumentException(
+ "RemoteViews used in a RemoteCollectionItems cannot specify separate "
+ + "layouts for orientations or sizes.");
+ }
+ mIds.add(id);
+ mViews.add(view);
+ return this;
+ }
+
+ /**
+ * Sets whether the item ids are stable across changes to the underlying data.
+ *
+ * @see android.widget.Adapter#hasStableIds()
+ */
+ @NonNull
+ public Builder setHasStableIds(boolean hasStableIds) {
+ mHasStableIds = hasStableIds;
+ return this;
+ }
+
+ /**
+ * Sets the view type count for the collection when used in an adapter. This can be set
+ * to the maximum number of different layout ids that will be used by RemoteViews in
+ * this collection.
+ *
+ * If this value is not set, then a value will be inferred from the provided items. As
+ * a result, the adapter may need to be recreated when the list is updated with
+ * previously unseen RemoteViews layouts for new items.
+ *
+ * @see android.widget.Adapter#getViewTypeCount()
+ */
+ @NonNull
+ public Builder setViewTypeCount(int viewTypeCount) {
+ mViewTypeCount = viewTypeCount;
+ return this;
+ }
+
+ /** Creates the {@link RemoteCollectionItems} defined by this builder. */
+ @NonNull
+ public RemoteCollectionItems build() {
+ if (mViewTypeCount < 1) {
+ // If a view type count wasn't specified, set it to be the number of distinct
+ // layout ids used in the items.
+ mViewTypeCount = (int) mViews.stream()
+ .mapToInt(RemoteViews::getLayoutId)
+ .distinct()
+ .count();
+ }
+ return new RemoteCollectionItems(
+ mIds.toArray(),
+ mViews.toArray(new RemoteViews[0]),
+ mHasStableIds,
+ Math.max(mViewTypeCount, 1));
+ }
+ }
+ }
+
/**
* Set the ID of the top-level view of the XML layout.
*
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index d59a415..2464b4a 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -26,6 +26,7 @@
import android.text.style.SuggestionSpan;
import android.util.Log;
import android.util.LruCache;
+import android.util.Range;
import android.view.textservice.SentenceSuggestionsInfo;
import android.view.textservice.SpellCheckerSession;
import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener;
@@ -62,7 +63,8 @@
// Pause between each spell check to keep the UI smooth
private final static int SPELL_PAUSE_DURATION = 400; // milliseconds
- private static final int MIN_SENTENCE_LENGTH = 50;
+ // The maximum length of sentence.
+ private static final int MAX_SENTENCE_LENGTH = WORD_ITERATOR_INTERVAL;
private static final int USE_SPAN_RANGE = -1;
@@ -89,7 +91,7 @@
// Shared by all SpellParsers. Cannot be shared with TextView since it may be used
// concurrently due to the asynchronous nature of onGetSuggestions.
- private WordIterator mWordIterator;
+ private SentenceIteratorWrapper mSentenceIterator;
@Nullable
private TextServicesManager mTextServicesManager;
@@ -151,8 +153,9 @@
resetSession();
if (locale != null) {
- // Change SpellParsers' wordIterator locale
- mWordIterator = new WordIterator(locale);
+ // Change SpellParsers' sentenceIterator locale
+ mSentenceIterator = new SentenceIteratorWrapper(
+ BreakIterator.getSentenceInstance(locale));
}
// This class is the listener for locale change: warn other locale-aware objects
@@ -306,22 +309,30 @@
final int start = editable.getSpanStart(spellCheckSpan);
final int end = editable.getSpanEnd(spellCheckSpan);
- // Do not check this word if the user is currently editing it
- final boolean isEditing;
+ // Check the span if any of following conditions is met:
+ // - the user is not currently editing it
+ // - or `forceCheckWhenEditingWord` is true.
+ final boolean isNotEditing;
// Defer spell check when typing a word ending with a punctuation like an apostrophe
// which could end up being a mid-word punctuation.
if (selectionStart == end + 1
&& WordIterator.isMidWordPunctuation(
mCurrentLocale, Character.codePointBefore(editable, end + 1))) {
- isEditing = false;
- } else {
+ isNotEditing = false;
+ } else if (selectionEnd <= start || selectionStart > end) {
// Allow the overlap of the cursor and the first boundary of the spell check span
// no to skip the spell check of the following word because the
// following word will never be spell-checked even if the user finishes composing
- isEditing = selectionEnd <= start || selectionStart > end;
+ isNotEditing = true;
+ } else {
+ // When cursor is at the end of spell check span, allow spell check if the
+ // character before cursor is a separator.
+ isNotEditing = selectionStart == end
+ && selectionStart > 0
+ && isSeparator(Character.codePointBefore(editable, selectionStart));
}
- if (start >= 0 && end > start && (forceCheckWhenEditingWord || isEditing)) {
+ if (start >= 0 && end > start && (forceCheckWhenEditingWord || isNotEditing)) {
spellCheckSpan.setSpellCheckInProgress(true);
final TextInfo textInfo = new TextInfo(editable, start, end, mCookie, mIds[i]);
textInfos[textInfosCount++] = textInfo;
@@ -346,6 +357,19 @@
}
}
+ private static boolean isSeparator(int codepoint) {
+ final int type = Character.getType(codepoint);
+ return ((1 << type) & ((1 << Character.SPACE_SEPARATOR)
+ | (1 << Character.LINE_SEPARATOR)
+ | (1 << Character.PARAGRAPH_SEPARATOR)
+ | (1 << Character.DASH_PUNCTUATION)
+ | (1 << Character.END_PUNCTUATION)
+ | (1 << Character.FINAL_QUOTE_PUNCTUATION)
+ | (1 << Character.INITIAL_QUOTE_PUNCTUATION)
+ | (1 << Character.START_PUNCTUATION)
+ | (1 << Character.OTHER_PUNCTUATION))) != 0;
+ }
+
private SpellCheckSpan onGetSuggestionsInternal(
SuggestionsInfo suggestionsInfo, int offset, int length) {
if (suggestionsInfo == null || suggestionsInfo.getCookie() != mCookie) {
@@ -534,6 +558,60 @@
mTextView.invalidateRegion(start, end, false /* No cursor involved */);
}
+ /**
+ * A wrapper of sentence iterator which only processes the specified window of the given text.
+ */
+ private static class SentenceIteratorWrapper {
+ private BreakIterator mSentenceIterator;
+ private int mStartOffset;
+ private int mEndOffset;
+
+ SentenceIteratorWrapper(BreakIterator sentenceIterator) {
+ mSentenceIterator = sentenceIterator;
+ }
+
+ /**
+ * Set the char sequence and the text window to process.
+ */
+ public void setCharSequence(CharSequence sequence, int start, int end) {
+ mStartOffset = Math.max(0, start);
+ mEndOffset = Math.min(end, sequence.length());
+ mSentenceIterator.setText(sequence.subSequence(mStartOffset, mEndOffset).toString());
+ }
+
+ /**
+ * See {@link BreakIterator#preceding(int)}
+ */
+ public int preceding(int offset) {
+ if (offset < mStartOffset) {
+ return BreakIterator.DONE;
+ }
+ int result = mSentenceIterator.preceding(offset - mStartOffset);
+ return result == BreakIterator.DONE ? BreakIterator.DONE : result + mStartOffset;
+ }
+
+ /**
+ * See {@link BreakIterator#following(int)}
+ */
+ public int following(int offset) {
+ if (offset > mEndOffset) {
+ return BreakIterator.DONE;
+ }
+ int result = mSentenceIterator.following(offset - mStartOffset);
+ return result == BreakIterator.DONE ? BreakIterator.DONE : result + mStartOffset;
+ }
+
+ /**
+ * See {@link BreakIterator#isBoundary(int)}
+ */
+ public boolean isBoundary(int offset) {
+ if (offset < mStartOffset || offset > mEndOffset) {
+ return false;
+ }
+ return mSentenceIterator.isBoundary(offset - mStartOffset);
+ }
+ }
+
private class SpellParser {
private Object mRange = new Object();
@@ -582,27 +660,15 @@
public void parse() {
Editable editable = (Editable) mTextView.getText();
- // Iterate over the newly added text and schedule new SpellCheckSpans
- final int start = Math.max(
- 0, editable.getSpanStart(mRange) - MIN_SENTENCE_LENGTH);
+ final int textChangeStart = editable.getSpanStart(mRange);
+ final int textChangeEnd = editable.getSpanEnd(mRange);
- final int end = editable.getSpanEnd(mRange);
+ Range<Integer> sentenceBoundary = detectSentenceBoundary(editable, textChangeStart,
+ textChangeEnd);
+ int sentenceStart = sentenceBoundary.getLower();
+ int sentenceEnd = sentenceBoundary.getUpper();
- int wordIteratorWindowEnd = Math.min(end, start + WORD_ITERATOR_INTERVAL);
- mWordIterator.setCharSequence(editable, start, wordIteratorWindowEnd);
-
- // Move back to the beginning of the current word, if any
- int wordStart = mWordIterator.preceding(start);
- int wordEnd;
- if (wordStart == BreakIterator.DONE) {
- wordEnd = mWordIterator.following(start);
- if (wordEnd != BreakIterator.DONE) {
- wordStart = mWordIterator.getBeginning(wordEnd);
- }
- } else {
- wordEnd = mWordIterator.getEnd(wordStart);
- }
- if (wordEnd == BreakIterator.DONE) {
+ if (sentenceStart == sentenceEnd) {
if (DBG) {
Log.i(TAG, "No more spell check.");
}
@@ -612,29 +678,16 @@
boolean scheduleOtherSpellCheck = false;
- if (wordIteratorWindowEnd < end) {
+ if (sentenceEnd < textChangeEnd) {
if (DBG) {
Log.i(TAG, "schedule other spell check.");
}
// Several batches needed on that region. Cut after last previous word
scheduleOtherSpellCheck = true;
}
- int spellCheckEnd = mWordIterator.preceding(wordIteratorWindowEnd);
- boolean correct = spellCheckEnd != BreakIterator.DONE;
- if (correct) {
- spellCheckEnd = mWordIterator.getEnd(spellCheckEnd);
- correct = spellCheckEnd != BreakIterator.DONE;
- }
- if (!correct) {
- if (DBG) {
- Log.i(TAG, "Incorrect range span.");
- }
- stop();
- return;
- }
+ int spellCheckEnd = sentenceEnd;
do {
- // TODO: Find the start position of the sentence.
- int spellCheckStart = wordStart;
+ int spellCheckStart = sentenceStart;
boolean createSpellCheckSpan = true;
// Cancel or merge overlapped spell check spans
for (int i = 0; i < mLength; ++i) {
@@ -671,27 +724,23 @@
}
// Stop spell checking when there are no characters in the range.
- if (spellCheckEnd < start) {
- break;
- }
if (spellCheckEnd <= spellCheckStart) {
Log.w(TAG, "Trying to spellcheck invalid region, from "
- + start + " to " + end);
+ + sentenceStart + " to " + spellCheckEnd);
break;
}
if (createSpellCheckSpan) {
addSpellCheckSpan(editable, spellCheckStart, spellCheckEnd);
}
} while (false);
- wordStart = spellCheckEnd;
-
- if (scheduleOtherSpellCheck && wordStart != BreakIterator.DONE && wordStart <= end) {
+ sentenceStart = spellCheckEnd;
+ if (scheduleOtherSpellCheck && sentenceStart != BreakIterator.DONE
+ && sentenceStart <= textChangeEnd) {
// Update range span: start new spell check from last wordStart
- setRangeSpan(editable, wordStart, end);
+ setRangeSpan(editable, sentenceStart, textChangeEnd);
} else {
removeRangeSpan(editable);
}
-
spellCheck(mForceCheckWhenEditingWord);
}
@@ -708,6 +757,94 @@
}
}
+ private Range<Integer> detectSentenceBoundary(CharSequence sequence,
+ int textChangeStart, int textChangeEnd) {
+ // Only process a substring of the full text due to performance concern.
+ final int iteratorWindowStart = findSeparator(sequence,
+ Math.max(0, textChangeStart - MAX_SENTENCE_LENGTH),
+ Math.max(0, textChangeStart - 2 * MAX_SENTENCE_LENGTH));
+ final int iteratorWindowEnd = findSeparator(sequence,
+ Math.min(textChangeStart + 2 * MAX_SENTENCE_LENGTH, textChangeEnd),
+ Math.min(textChangeStart + 3 * MAX_SENTENCE_LENGTH, sequence.length()));
+ if (DBG) {
+ Log.d(TAG, "Set iterator window as [" + iteratorWindowStart + ", " + iteratorWindowEnd
+ + ").");
+ }
+ mSentenceIterator.setCharSequence(sequence, iteratorWindowStart, iteratorWindowEnd);
+
+ // Detect the offset of sentence begin/end on the substring.
+ int sentenceStart = mSentenceIterator.isBoundary(textChangeStart) ? textChangeStart
+ : mSentenceIterator.preceding(textChangeStart);
+ int sentenceEnd = mSentenceIterator.following(sentenceStart);
+ if (sentenceEnd == BreakIterator.DONE) {
+ sentenceEnd = iteratorWindowEnd;
+ }
+ if (DBG) {
+ if (sentenceStart != sentenceEnd) {
+ Log.d(TAG, "Sentence detected [" + sentenceStart + ", " + sentenceEnd + ").");
+ }
+ }
+
+ if (sentenceEnd - sentenceStart <= MAX_SENTENCE_LENGTH) {
+ // Add more sentences until the MAX_SENTENCE_LENGTH limitation is reached.
+ while (sentenceEnd < textChangeEnd) {
+ int nextEnd = mSentenceIterator.following(sentenceEnd);
+ if (nextEnd == BreakIterator.DONE
+ || nextEnd - sentenceStart > MAX_SENTENCE_LENGTH) {
+ break;
+ }
+ sentenceEnd = nextEnd;
+ }
+ } else {
+ // If the sentence containing `textChangeStart` is longer than MAX_SENTENCE_LENGTH,
+ // the sentence will be sliced into sub-sentences of about MAX_SENTENCE_LENGTH
+ // characters each. This is done by processing the unchecked part of that sentence :
+ // [textChangeStart, sentenceEnd)
+ //
+ // - If the `uncheckedLength` is bigger than MAX_SENTENCE_LENGTH, then check the
+ // [textChangeStart, textChangeStart + MAX_SENTENCE_LENGTH), and leave the rest
+ // part for the next check.
+ //
+ // - If the `uncheckedLength` is smaller than or equal to MAX_SENTENCE_LENGTH,
+ // then check [sentenceEnd - MAX_SENTENCE_LENGTH, sentenceEnd).
+ //
+ // The offset should be rounded up to word boundary.
+ int uncheckedLength = sentenceEnd - textChangeStart;
+ if (uncheckedLength > MAX_SENTENCE_LENGTH) {
+ sentenceEnd = findSeparator(sequence, sentenceStart + MAX_SENTENCE_LENGTH,
+ sentenceEnd);
+ sentenceStart = roundUpToWordStart(sequence, textChangeStart, sentenceStart);
+ } else {
+ sentenceStart = roundUpToWordStart(sequence, sentenceEnd - MAX_SENTENCE_LENGTH,
+ sentenceStart);
+ }
+ }
+ return new Range(sentenceStart, sentenceEnd);
+ }
+
+ private int roundUpToWordStart(CharSequence sequence, int position, int frontBoundary) {
+ if (isSeparator(sequence.charAt(position))) {
+ return position;
+ }
+ int separator = findSeparator(sequence, position, frontBoundary);
+ return separator != frontBoundary ? separator + 1 : frontBoundary;
+ }
+
+ /**
+ * Search the range [start, end) of sequence and returns the position of the first separator.
+ * If end is smaller than start, do a reverse search.
+ * Returns `end` if no separator is found.
+ */
+ private static int findSeparator(CharSequence sequence, int start, int end) {
+ final int step = start < end ? 1 : -1;
+ for (int i = start; i != end; i += step) {
+ if (isSeparator(sequence.charAt(i))) {
+ return i;
+ }
+ }
+ return end;
+ }
+
public static boolean haveWordBoundariesChanged(final Editable editable, final int start,
final int end, final int spanStart, final int spanEnd) {
final boolean haveWordBoundariesChanged;
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index ee98878..52801fa 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -474,6 +474,12 @@
public static final String SHARE_USE_SERVICE_TARGETS = "share_use_service_targets";
+ /*
+ * (long) The duration that the home button must be pressed before triggering Assist
+ */
+ public static final String HOME_BUTTON_LONG_PRESS_DURATION_MS =
+ "home_button_long_press_duration_ms";
+
private SystemUiDeviceConfigFlags() {
}
}
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index d0c807d..783d088 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -221,10 +221,6 @@
dispatchMessage(mH.obtainMessage(DO_GET_SURROUNDING_TEXT, flags, 0 /* unused */, args));
}
- public void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
- // no-op
- }
-
public void getCursorCapsMode(int reqModes, IIntResultCallback callback) {
dispatchMessage(
mH.obtainMessage(DO_GET_CURSOR_CAPS_MODE, reqModes, 0 /* unused */, callback));
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index f29e95c..c9755a3 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -204,6 +204,8 @@
@Nullable
private final float[] mActivityViewToScreenMatrixValues;
+ public final boolean isInputMethodSuppressingSpellChecker;
+
/**
* @return {@link Matrix} that corresponds to {@link #mActivityViewToScreenMatrixValues}.
* {@code null} if {@link #mActivityViewToScreenMatrixValues} is {@code null}.
@@ -220,7 +222,8 @@
public InputBindResult(@ResultCode int _result,
IInputMethodSession _method, InputChannel _channel, String _id, int _sequence,
- @Nullable Matrix activityViewToScreenMatrix) {
+ @Nullable Matrix activityViewToScreenMatrix,
+ boolean isInputMethodSuppressingSpellChecker) {
result = _result;
method = _method;
channel = _channel;
@@ -232,6 +235,7 @@
mActivityViewToScreenMatrixValues = new float[9];
activityViewToScreenMatrix.getValues(mActivityViewToScreenMatrixValues);
}
+ this.isInputMethodSuppressingSpellChecker = isInputMethodSuppressingSpellChecker;
}
InputBindResult(Parcel source) {
@@ -245,6 +249,7 @@
id = source.readString();
sequence = source.readInt();
mActivityViewToScreenMatrixValues = source.createFloatArray();
+ isInputMethodSuppressingSpellChecker = source.readBoolean();
}
@Override
@@ -252,6 +257,7 @@
return "InputBindResult{result=" + getResultString() + " method="+ method + " id=" + id
+ " sequence=" + sequence
+ " activityViewToScreenMatrix=" + getActivityViewToScreenMatrix()
+ + " isInputMethodSuppressingSpellChecker=" + isInputMethodSuppressingSpellChecker
+ "}";
}
@@ -274,6 +280,7 @@
dest.writeString(id);
dest.writeInt(sequence);
dest.writeFloatArray(mActivityViewToScreenMatrixValues);
+ dest.writeBoolean(isInputMethodSuppressingSpellChecker);
}
/**
@@ -340,7 +347,7 @@
}
private static InputBindResult error(@ResultCode int result) {
- return new InputBindResult(result, null, null, null, -1, null);
+ return new InputBindResult(result, null, null, null, -1, null, false);
}
/**
diff --git a/core/java/com/android/internal/widget/CallLayout.java b/core/java/com/android/internal/widget/CallLayout.java
index 6cc5a4a..83345da 100644
--- a/core/java/com/android/internal/widget/CallLayout.java
+++ b/core/java/com/android/internal/widget/CallLayout.java
@@ -100,7 +100,6 @@
}
// TODO(b/179178086): crop/clip the icon to a circle?
mConversationIconView.setImageIcon(icon);
- mConversationText.setText(callerName);
}
@RemotableViewMethod
diff --git a/core/java/com/android/internal/widget/RecyclerView.java b/core/java/com/android/internal/widget/RecyclerView.java
index 89a90e9..17ce75e 100644
--- a/core/java/com/android/internal/widget/RecyclerView.java
+++ b/core/java/com/android/internal/widget/RecyclerView.java
@@ -16,10 +16,16 @@
package com.android.internal.widget;
+import static android.widget.EdgeEffect.TYPE_GLOW;
+import static android.widget.EdgeEffect.TYPE_STRETCH;
+import static android.widget.EdgeEffect.USE_STRETCH_EDGE_EFFECT_BY_DEFAULT;
+import static android.widget.EdgeEffect.USE_STRETCH_EDGE_EFFECT_FOR_SUPPORTED;
+
import android.annotation.CallSuper;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.compat.Compatibility;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
@@ -460,6 +466,8 @@
private final int[] mScrollConsumed = new int[2];
private final int[] mNestedOffsets = new int[2];
+ private int mEdgeEffectType;
+
/**
* These are views that had their a11y importance changed during a layout. We defer these events
* until the end of the layout because a11y service may make sync calls back to the RV while
@@ -587,6 +595,14 @@
setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
}
+ boolean defaultToStretch = Compatibility.isChangeEnabled(USE_STRETCH_EDGE_EFFECT_BY_DEFAULT)
+ || Compatibility.isChangeEnabled(USE_STRETCH_EDGE_EFFECT_FOR_SUPPORTED);
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.EdgeEffect);
+ mEdgeEffectType = a.getInt(com.android.internal.R.styleable.EdgeEffect_edgeEffectType,
+ defaultToStretch ? TYPE_STRETCH : TYPE_GLOW);
+ a.recycle();
+
// Re-set whether nested scrolling is enabled so that it is set on all API levels
setNestedScrollingEnabled(nestedScrollingEnabled);
}
@@ -610,6 +626,28 @@
}
/**
+ * Returns the {@link EdgeEffect#getType()} used for all EdgeEffects.
+ *
+ * @return @link EdgeEffect#getType()} used for all EdgeEffects.
+ */
+ @EdgeEffect.EdgeEffectType
+ public int getEdgeEffectType() {
+ return mEdgeEffectType;
+ }
+
+ /**
+ * Sets the {@link EdgeEffect#getType()} used in all EdgeEffects.
+ * Any existing over-scroll effects are cleared and new effects are created as needed.
+ *
+ * @param type the {@link EdgeEffect#getType()} used in all EdgeEffects.
+ */
+ public void setEdgeEffectType(@EdgeEffect.EdgeEffectType int type) {
+ mEdgeEffectType = type;
+ invalidateGlows();
+ invalidate();
+ }
+
+ /**
* Instantiate and set a LayoutManager, if specified in the attributes.
*/
private void createLayoutManager(Context context, String className, AttributeSet attrs,
@@ -2183,6 +2221,7 @@
return;
}
mLeftGlow = new EdgeEffect(getContext());
+ mLeftGlow.setType(mEdgeEffectType);
if (mClipToPadding) {
mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
@@ -2196,6 +2235,7 @@
return;
}
mRightGlow = new EdgeEffect(getContext());
+ mRightGlow.setType(mEdgeEffectType);
if (mClipToPadding) {
mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
@@ -2209,6 +2249,7 @@
return;
}
mTopGlow = new EdgeEffect(getContext());
+ mTopGlow.setType(mEdgeEffectType);
if (mClipToPadding) {
mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
@@ -2223,6 +2264,7 @@
return;
}
mBottomGlow = new EdgeEffect(getContext());
+ mBottomGlow.setType(mEdgeEffectType);
if (mClipToPadding) {
mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
@@ -2663,7 +2705,7 @@
mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
- if (mScrollState == SCROLL_STATE_SETTLING) {
+ if (stopGlowAnimations(e) || mScrollState == SCROLL_STATE_SETTLING) {
getParent().requestDisallowInterceptTouchEvent(true);
setScrollState(SCROLL_STATE_DRAGGING);
}
@@ -2731,6 +2773,38 @@
return mScrollState == SCROLL_STATE_DRAGGING;
}
+ /**
+ * This stops any edge glow animation that is currently running by applying a
+ * 0 length pull at the displacement given by the provided MotionEvent. On pre-S devices,
+ * this method does nothing, allowing any animating edge effect to continue animating and
+ * returning <code>false</code> always.
+ *
+ * @param e The motion event to use to indicate the finger position for the displacement of
+ * the current pull.
+ * @return <code>true</code> if any edge effect had an existing effect to be drawn ond the
+ * animation was stopped or <code>false</code> if no edge effect had a value to display.
+ */
+ private boolean stopGlowAnimations(MotionEvent e) {
+ boolean stopped = false;
+ if (mLeftGlow != null && mLeftGlow.getDistance() != 0) {
+ mLeftGlow.onPullDistance(0, 1 - (e.getY() / getHeight()));
+ stopped = true;
+ }
+ if (mRightGlow != null && mRightGlow.getDistance() != 0) {
+ mRightGlow.onPullDistance(0, e.getY() / getHeight());
+ stopped = true;
+ }
+ if (mTopGlow != null && mTopGlow.getDistance() != 0) {
+ mTopGlow.onPullDistance(0, e.getX() / getWidth());
+ stopped = true;
+ }
+ if (mBottomGlow != null && mBottomGlow.getDistance() != 0) {
+ mBottomGlow.onPullDistance(0, 1 - e.getX() / getWidth());
+ stopped = true;
+ }
+ return stopped;
+ }
+
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
final int listenerCount = mOnItemTouchListeners.size();
@@ -2807,6 +2881,8 @@
final int y = (int) (e.getY(index) + 0.5f);
int dx = mLastTouchX - x;
int dy = mLastTouchY - y;
+ dx -= releaseHorizontalGlow(dx, e.getY());
+ dy -= releaseVerticalGlow(dy, e.getX());
if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {
dx -= mScrollConsumed[0];
@@ -2887,6 +2963,72 @@
return true;
}
+ /**
+ * If either of the horizontal edge glows are currently active, this consumes part or all of
+ * deltaX on the edge glow.
+ *
+ * @param deltaX The pointer motion, in pixels, in the horizontal direction, positive
+ * for moving down and negative for moving up.
+ * @param y The vertical position of the pointer.
+ * @return The amount of <code>deltaX</code> that has been consumed by the
+ * edge glow.
+ */
+ private int releaseHorizontalGlow(int deltaX, float y) {
+ // First allow releasing existing overscroll effect:
+ float consumed = 0;
+ float displacement = y / getHeight();
+ float pullDistance = (float) deltaX / getWidth();
+ if (mLeftGlow != null && mLeftGlow.getDistance() != 0) {
+ consumed = -mLeftGlow.onPullDistance(-pullDistance, 1 - displacement);
+ if (mLeftGlow.getDistance() == 0) {
+ mLeftGlow.onRelease();
+ }
+ } else if (mRightGlow != null && mRightGlow.getDistance() != 0) {
+ consumed = mRightGlow.onPullDistance(pullDistance, displacement);
+ if (mRightGlow.getDistance() == 0) {
+ mRightGlow.onRelease();
+ }
+ }
+ int pixelsConsumed = Math.round(consumed * getWidth());
+ if (pixelsConsumed != 0) {
+ invalidate();
+ }
+ return pixelsConsumed;
+ }
+
+ /**
+ * If either of the vertical edge glows are currently active, this consumes part or all of
+ * deltaY on the edge glow.
+ *
+ * @param deltaY The pointer motion, in pixels, in the vertical direction, positive
+ * for moving down and negative for moving up.
+ * @param x The vertical position of the pointer.
+ * @return The amount of <code>deltaY</code> that has been consumed by the
+ * edge glow.
+ */
+ private int releaseVerticalGlow(int deltaY, float x) {
+ // First allow releasing existing overscroll effect:
+ float consumed = 0;
+ float displacement = x / getWidth();
+ float pullDistance = (float) deltaY / getHeight();
+ if (mTopGlow != null && mTopGlow.getDistance() != 0) {
+ consumed = -mTopGlow.onPullDistance(-pullDistance, displacement);
+ if (mTopGlow.getDistance() == 0) {
+ mTopGlow.onRelease();
+ }
+ } else if (mBottomGlow != null && mBottomGlow.getDistance() != 0) {
+ consumed = mBottomGlow.onPullDistance(pullDistance, 1 - displacement);
+ if (mBottomGlow.getDistance() == 0) {
+ mBottomGlow.onRelease();
+ }
+ }
+ int pixelsConsumed = Math.round(consumed * getHeight());
+ if (pixelsConsumed != 0) {
+ invalidate();
+ }
+ return pixelsConsumed;
+ }
+
private void resetTouch() {
if (mVelocityTracker != null) {
mVelocityTracker.clear();
diff --git a/core/jni/android_net_NetworkUtils.cpp b/core/jni/android_net_NetworkUtils.cpp
index 7508108..a781a37 100644
--- a/core/jni/android_net_NetworkUtils.cpp
+++ b/core/jni/android_net_NetworkUtils.cpp
@@ -52,27 +52,6 @@
// FrameworkListener limits the size of commands to 4096 bytes.
constexpr int MAXCMDSIZE = 4096;
-static void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
- ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName));
- if (detailMessage.get() == NULL) {
- // Not really much we can do here. We're probably dead in the water,
- // but let's try to stumble on...
- env->ExceptionClear();
- }
- static jclass errnoExceptionClass =
- MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/system/ErrnoException"));
-
- static jmethodID errnoExceptionCtor =
- GetMethodIDOrDie(env, errnoExceptionClass,
- "<init>", "(Ljava/lang/String;I)V");
-
- jobject exception = env->NewObject(errnoExceptionClass,
- errnoExceptionCtor,
- detailMessage.get(),
- error);
- env->Throw(reinterpret_cast<jthrowable>(exception));
-}
-
static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
{
struct sock_filter filter_code[] = {
@@ -150,7 +129,7 @@
int fd = resNetworkQuery(netId, queryname.data(), ns_class, ns_type, flags);
if (fd < 0) {
- throwErrnoException(env, "resNetworkQuery", -fd);
+ jniThrowErrnoException(env, "resNetworkQuery", -fd);
return nullptr;
}
@@ -165,7 +144,7 @@
int fd = resNetworkSend(netId, data, msgLen, flags);
if (fd < 0) {
- throwErrnoException(env, "resNetworkSend", -fd);
+ jniThrowErrnoException(env, "resNetworkSend", -fd);
return nullptr;
}
@@ -180,13 +159,13 @@
int res = resNetworkResult(fd, &rcode, buf.data(), MAXPACKETSIZE);
jniSetFileDescriptorOfFD(env, javaFd, -1);
if (res < 0) {
- throwErrnoException(env, "resNetworkResult", -res);
+ jniThrowErrnoException(env, "resNetworkResult", -res);
return nullptr;
}
jbyteArray answer = env->NewByteArray(res);
if (answer == nullptr) {
- throwErrnoException(env, "resNetworkResult", ENOMEM);
+ jniThrowErrnoException(env, "resNetworkResult", ENOMEM);
return nullptr;
} else {
env->SetByteArrayRegion(answer, 0, res,
@@ -208,7 +187,7 @@
static jobject android_net_utils_getDnsNetwork(JNIEnv *env, jobject thiz) {
unsigned dnsNetId = 0;
if (int res = getNetworkForDns(&dnsNetId) < 0) {
- throwErrnoException(env, "getDnsNetId", -res);
+ jniThrowErrnoException(env, "getDnsNetId", -res);
return nullptr;
}
bool privateDnsBypass = dnsNetId & NETID_USE_LOCAL_NAMESERVERS;
@@ -233,8 +212,8 @@
// Obtain the parameters of the TCP repair window.
int rc = getsockopt(fd, IPPROTO_TCP, TCP_REPAIR_WINDOW, &trw, &size);
if (rc == -1) {
- throwErrnoException(env, "getsockopt : TCP_REPAIR_WINDOW", errno);
- return NULL;
+ jniThrowErrnoException(env, "getsockopt : TCP_REPAIR_WINDOW", errno);
+ return NULL;
}
struct tcp_info tcpinfo = {};
@@ -244,8 +223,8 @@
// should be applied to the window size.
rc = getsockopt(fd, IPPROTO_TCP, TCP_INFO, &tcpinfo, &tcpinfo_size);
if (rc == -1) {
- throwErrnoException(env, "getsockopt : TCP_INFO", errno);
- return NULL;
+ jniThrowErrnoException(env, "getsockopt : TCP_INFO", errno);
+ return NULL;
}
jclass class_TcpRepairWindow = env->FindClass("android/net/TcpRepairWindow");
diff --git a/core/jni/android_os_SharedMemory.cpp b/core/jni/android_os_SharedMemory.cpp
index dc86187..fe375f4 100644
--- a/core/jni/android_os_SharedMemory.cpp
+++ b/core/jni/android_os_SharedMemory.cpp
@@ -35,21 +35,6 @@
jclass errnoExceptionClass;
jmethodID errnoExceptionCtor; // MethodID for ErrnoException.<init>(String,I)
-void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
- ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName));
- if (detailMessage.get() == NULL) {
- // Not really much we can do here. We're probably dead in the water,
- // but let's try to stumble on...
- env->ExceptionClear();
- }
-
- jobject exception = env->NewObject(errnoExceptionClass,
- errnoExceptionCtor,
- detailMessage.get(),
- error);
- env->Throw(reinterpret_cast<jthrowable>(exception));
-}
-
jobject SharedMemory_nCreate(JNIEnv* env, jobject, jstring jname, jint size) {
// Name is optional so we can't use ScopedUtfChars for this as it throws NPE on null
@@ -65,7 +50,7 @@
}
if (fd < 0) {
- throwErrnoException(env, "SharedMemory_create", err);
+ jniThrowErrnoException(env, "SharedMemory_create", err);
return nullptr;
}
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index dcfa950..d4b5c2b 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -1316,24 +1316,6 @@
return removeAllProcessGroups();
}
-static void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
- ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName));
- if (detailMessage.get() == NULL) {
- // Not really much we can do here. We're probably dead in the water,
- // but let's try to stumble on...
- env->ExceptionClear();
- }
- static jclass errnoExceptionClass =
- MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/system/ErrnoException"));
-
- static jmethodID errnoExceptionCtor =
- GetMethodIDOrDie(env, errnoExceptionClass, "<init>", "(Ljava/lang/String;I)V");
-
- jobject exception =
- env->NewObject(errnoExceptionClass, errnoExceptionCtor, detailMessage.get(), error);
- env->Throw(reinterpret_cast<jthrowable>(exception));
-}
-
// Wrapper function to the syscall pidfd_open, which creates a file
// descriptor that refers to the process whose PID is specified in pid.
static inline int sys_pidfd_open(pid_t pid, unsigned int flags) {
@@ -1343,7 +1325,7 @@
static jint android_os_Process_nativePidFdOpen(JNIEnv* env, jobject, jint pid, jint flags) {
int fd = sys_pidfd_open(pid, flags);
if (fd < 0) {
- throwErrnoException(env, "nativePidFdOpen", errno);
+ jniThrowErrnoException(env, "nativePidFdOpen", errno);
return -1;
}
return fd;
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index ab6633f..bfeb01d 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -45,12 +45,6 @@
return value ? "true" : "false";
}
-enum class HandleEventResponse : int {
- // Allowed return values of 'handleEvent' function as documented in LooperCallback::handleEvent
- REMOVE_CALLBACK = 0,
- KEEP_CALLBACK = 1
-};
-
static struct {
jclass clazz;
@@ -77,14 +71,6 @@
return str;
}
-/**
- * Convert an enumeration to its underlying type. Replace with std::to_underlying when available.
- */
-template <class T>
-static std::underlying_type_t<T> toUnderlying(const T& t) {
- return static_cast<std::underlying_type_t<T>>(t);
-}
-
class NativeInputEventReceiver : public LooperCallback {
public:
NativeInputEventReceiver(JNIEnv* env, jobject receiverWeak,
@@ -121,16 +107,11 @@
return mInputConsumer.getChannel()->getName();
}
- HandleEventResponse processOutboundEvents();
+ status_t processOutboundEvents();
// From 'LooperCallback'
int handleEvent(int receiveFd, int events, void* data) override;
};
-// Ensure HandleEventResponse underlying type matches the return type of LooperCallback::handleEvent
-static_assert(std::is_same<std::underlying_type_t<HandleEventResponse>,
- std::invoke_result_t<decltype(&LooperCallback::handleEvent),
- NativeInputEventReceiver, int, int, void*>>::value);
-
NativeInputEventReceiver::NativeInputEventReceiver(
JNIEnv* env, jobject receiverWeak, const std::shared_ptr<InputChannel>& inputChannel,
const sp<MessageQueue>& messageQueue)
@@ -167,26 +148,12 @@
ALOGD("channel '%s' ~ Finished input event.", getInputChannelName().c_str());
}
- status_t status = mInputConsumer.sendFinishedSignal(seq, handled);
- if (status != OK) {
- if (status == WOULD_BLOCK) {
- if (kDebugDispatchCycle) {
- ALOGD("channel '%s' ~ Could not send finished signal immediately. "
- "Enqueued for later.", getInputChannelName().c_str());
- }
- Finish finish;
- finish.seq = seq;
- finish.handled = handled;
- mFinishQueue.push_back(finish);
- if (mFinishQueue.size() == 1) {
- setFdEvents(ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT);
- }
- return OK;
- }
- ALOGW("Failed to send finished signal on channel '%s'. status=%d",
- getInputChannelName().c_str(), status);
- }
- return status;
+ Finish finish{
+ .seq = seq,
+ .handled = handled,
+ };
+ mFinishQueue.push_back(finish);
+ return processOutboundEvents();
}
void NativeInputEventReceiver::setFdEvents(int events) {
@@ -217,7 +184,7 @@
* InputPublisher. If no events are remaining, let the looper know so that it doesn't wake up
* unnecessarily.
*/
-HandleEventResponse NativeInputEventReceiver::processOutboundEvents() {
+status_t NativeInputEventReceiver::processOutboundEvents() {
while (!mFinishQueue.empty()) {
const Finish& finish = *mFinishQueue.begin();
status_t status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);
@@ -233,7 +200,8 @@
ALOGD("channel '%s' ~ Remaining outbound events: %zu.",
getInputChannelName().c_str(), mFinishQueue.size());
}
- return HandleEventResponse::KEEP_CALLBACK; // try again later
+ setFdEvents(ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT);
+ return WOULD_BLOCK; // try again later
}
// Some other error. Give up
@@ -247,42 +215,49 @@
jniThrowRuntimeException(env, message.c_str());
mMessageQueue->raiseAndClearException(env, "finishInputEvent");
}
- return HandleEventResponse::REMOVE_CALLBACK;
+ return status;
}
// The queue is now empty. Tell looper there's no more output to expect.
setFdEvents(ALOOPER_EVENT_INPUT);
- return HandleEventResponse::KEEP_CALLBACK;
+ return OK;
}
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
+ // Allowed return values of this function as documented in LooperCallback::handleEvent
+ constexpr int REMOVE_CALLBACK = 0;
+ constexpr int KEEP_CALLBACK = 1;
+
if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
// This error typically occurs when the publisher has closed the input channel
// as part of removing a window or finishing an IME session, in which case
// the consumer will soon be disposed as well.
if (kDebugDispatchCycle) {
- ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred. "
- "events=0x%x", getInputChannelName().c_str(), events);
+ ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred. events=0x%x",
+ getInputChannelName().c_str(), events);
}
- return toUnderlying(HandleEventResponse::REMOVE_CALLBACK);
+ return REMOVE_CALLBACK;
}
if (events & ALOOPER_EVENT_INPUT) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);
mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
- return status == OK || status == NO_MEMORY
- ? toUnderlying(HandleEventResponse::KEEP_CALLBACK)
- : toUnderlying(HandleEventResponse::REMOVE_CALLBACK);
+ return status == OK || status == NO_MEMORY ? KEEP_CALLBACK : REMOVE_CALLBACK;
}
if (events & ALOOPER_EVENT_OUTPUT) {
- return toUnderlying(processOutboundEvents());
+ const status_t status = processOutboundEvents();
+ if (status == OK || status == WOULD_BLOCK) {
+ return KEEP_CALLBACK;
+ } else {
+ return REMOVE_CALLBACK;
+ }
}
- ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
- "events=0x%x", getInputChannelName().c_str(), events);
- return toUnderlying(HandleEventResponse::KEEP_CALLBACK);
+ ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. events=0x%x",
+ getInputChannelName().c_str(), events);
+ return KEEP_CALLBACK;
}
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
@@ -503,9 +478,13 @@
sp<NativeInputEventReceiver> receiver =
reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
status_t status = receiver->finishInputEvent(seq, handled);
- if (status && status != DEAD_OBJECT) {
+ if (status == OK || status == WOULD_BLOCK) {
+ return; // normal operation
+ }
+ if (status != DEAD_OBJECT) {
std::string message =
- android::base::StringPrintf("Failed to finish input event. status=%d", status);
+ android::base::StringPrintf("Failed to finish input event. status=%s(%d)",
+ strerror(-status), status);
jniThrowRuntimeException(env, message.c_str());
}
}
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 9746a07..52d21a8 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -34,6 +34,8 @@
#include "core_jni_helpers.h"
+using android::base::Result;
+
namespace android {
// Log debug messages about the dispatch cycle.
@@ -170,16 +172,16 @@
// as part of finishing an IME session, in which case the publisher will
// soon be disposed as well.
if (kDebugDispatchCycle) {
- ALOGD("channel '%s' ~ Consumer closed input channel or an error occurred. "
- "events=0x%x", getInputChannelName().c_str(), events);
+ ALOGD("channel '%s' ~ Consumer closed input channel or an error occurred. events=0x%x",
+ getInputChannelName().c_str(), events);
}
return 0; // remove the callback
}
if (!(events & ALOOPER_EVENT_INPUT)) {
- ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
- "events=0x%x", getInputChannelName().c_str(), events);
+ ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. events=0x%x",
+ getInputChannelName().c_str(), events);
return 1;
}
@@ -197,16 +199,9 @@
ScopedLocalRef<jobject> senderObj(env, NULL);
bool skipCallbacks = false;
for (;;) {
- uint32_t publishedSeq;
- bool handled;
- std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)> callback =
- [&publishedSeq, &handled](uint32_t inSeq, bool inHandled,
- nsecs_t inConsumeTime) -> void {
- publishedSeq = inSeq;
- handled = inHandled;
- };
- status_t status = mInputPublisher.receiveFinishedSignal(callback);
- if (status) {
+ Result<InputPublisher::Finished> result = mInputPublisher.receiveFinishedSignal();
+ if (!result.ok()) {
+ const status_t status = result.error().code();
if (status == WOULD_BLOCK) {
return OK;
}
@@ -215,7 +210,7 @@
return status;
}
- auto it = mPublishedSeqMap.find(publishedSeq);
+ auto it = mPublishedSeqMap.find(result->seq);
if (it == mPublishedSeqMap.end()) {
continue;
}
@@ -224,25 +219,24 @@
mPublishedSeqMap.erase(it);
if (kDebugDispatchCycle) {
- ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, "
- "pendingEvents=%zu.",
- getInputChannelName().c_str(), seq, handled ? "true" : "false",
- mPublishedSeqMap.size());
+ ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, pendingEvents=%zu.",
+ getInputChannelName().c_str(), seq, result->handled ? "true" : "false",
+ mPublishedSeqMap.size());
}
if (!skipCallbacks) {
if (!senderObj.get()) {
senderObj.reset(jniGetReferent(env, mSenderWeakGlobal));
if (!senderObj.get()) {
- ALOGW("channel '%s' ~ Sender object was finalized "
- "without being disposed.", getInputChannelName().c_str());
+ ALOGW("channel '%s' ~ Sender object was finalized without being disposed.",
+ getInputChannelName().c_str());
return DEAD_OBJECT;
}
}
env->CallVoidMethod(senderObj.get(),
- gInputEventSenderClassInfo.dispatchInputEventFinished,
- jint(seq), jboolean(handled));
+ gInputEventSenderClassInfo.dispatchInputEventFinished,
+ static_cast<jint>(seq), static_cast<jboolean>(result->handled));
if (env->ExceptionCheck()) {
ALOGE("Exception dispatching finished signal.");
skipCallbacks = true;
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 0bed29b..be17d92 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -825,7 +825,7 @@
PrepareDir(user_source, 0710, user_id ? AID_ROOT : AID_SHELL,
multiuser_get_uid(user_id, AID_EVERYBODY), fail_fn);
- bool isAppDataIsolationEnabled = GetBoolProperty(kVoldAppDataIsolation, false);
+ bool isAppDataIsolationEnabled = GetBoolProperty(kVoldAppDataIsolation, true);
if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH) {
const std::string pass_through_source = StringPrintf("/mnt/pass_through/%d", user_id);
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 632d372..dca6002 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -192,6 +192,12 @@
optional Camera camera = 12;
optional SettingProto carrier_apps_handled = 13 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+ message Clipboard {
+ optional SettingProto show_access_notifications = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+ optional Clipboard clipboard = 89;
+
optional SettingProto cmas_additional_broadcast_pkg = 14 [ (android.privacy).dest = DEST_AUTOMATIC ];
repeated SettingProto completed_categories = 15;
optional SettingProto connectivity_release_pending_intent_delay_ms = 16 [ (android.privacy).dest = DEST_AUTOMATIC ];
@@ -647,5 +653,5 @@
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 89;
+ // Next tag = 90;
}
diff --git a/core/proto/android/server/usagestatsservice.proto b/core/proto/android/server/usagestatsservice.proto
index e32c07f..3a959f1 100644
--- a/core/proto/android/server/usagestatsservice.proto
+++ b/core/proto/android/server/usagestatsservice.proto
@@ -57,6 +57,8 @@
// Time attributes stored as an offset of the IntervalStats's beginTime.
optional int64 last_time_visible_ms = 10;
optional int64 total_time_visible_ms = 11;
+ // Time attributes stored as an offset of the IntervalStats's beginTime.
+ optional int64 last_time_component_used_ms = 12;
}
// Stores the relevant information an IntervalStats will have about a Configuration
diff --git a/core/proto/android/server/usagestatsservice_v2.proto b/core/proto/android/server/usagestatsservice_v2.proto
index 664c22d..3e5cd92 100644
--- a/core/proto/android/server/usagestatsservice_v2.proto
+++ b/core/proto/android/server/usagestatsservice_v2.proto
@@ -81,6 +81,7 @@
optional int64 total_time_service_used_ms = 9;
optional int64 last_time_visible_ms = 10;
optional int64 total_time_visible_ms = 11;
+ optional int64 last_time_component_used_ms = 12;
}
/**
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f9227617..f135d67 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2775,11 +2775,11 @@
The app can check whether it has this authorization by calling
{@link android.provider.Settings#canDrawOverlays
Settings.canDrawOverlays()}.
- <p>Protection level: signature|appop|installer|appPredictor|pre23|development -->
+ <p>Protection level: signature|setup|appop|installer|appPredictor|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|appPredictor|pre23|development" />
+ android:protectionLevel="signature|setup|appop|installer|appPredictor|pre23|development" />
<!-- @SystemApi @hide Allows an application to create windows using the type
{@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY},
@@ -4365,6 +4365,11 @@
<permission android:name="android.permission.MODIFY_AUDIO_ROUTING"
android:protectionLevel="signature|privileged" />
+ <!-- @TestApi Allows an application to query audio related state.
+ @hide -->
+ <permission android:name="android.permission.QUERY_AUDIO_STATE"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to modify what effects are applied to all audio
(matching certain criteria) from any application.
<p>Not for use by third-party applications.</p>
diff --git a/core/res/res/layout/notification_expand_button.xml b/core/res/res/layout/notification_expand_button.xml
index f92e6d6..b969fa4 100644
--- a/core/res/res/layout/notification_expand_button.xml
+++ b/core/res/res/layout/notification_expand_button.xml
@@ -39,7 +39,7 @@
android:layout_height="@dimen/notification_expand_button_pill_height"
android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
android:gravity="center_vertical"
- android:paddingLeft="8dp"
+ android:paddingStart="8dp"
/>
<ImageView
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index 2d1c342..b9a3625 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -27,7 +27,7 @@
android:id="@+id/notification_action_list_margin_target"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/notification_action_list_height"
+ android:layout_marginBottom="@dimen/notification_content_margin"
android:orientation="vertical"
>
diff --git a/core/res/res/layout/notification_template_material_big_call.xml b/core/res/res/layout/notification_template_material_big_call.xml
new file mode 100644
index 0000000..1d50467
--- /dev/null
+++ b/core/res/res/layout/notification_template_material_big_call.xml
@@ -0,0 +1,107 @@
+<?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
+ -->
+<com.android.internal.widget.CallLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:tag="call"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ >
+
+ <!-- CallLayout shares visual appearance with ConversationLayout, so shares layouts -->
+ <include layout="@layout/notification_template_conversation_icon_container" />
+
+ <LinearLayout
+ android:id="@+id/notification_action_list_margin_target"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/notification_content_margin"
+ android:orientation="vertical"
+ >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ >
+
+ <LinearLayout
+ android:id="@+id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginStart="@dimen/conversation_content_start"
+ android:orientation="vertical"
+ android:minHeight="68dp"
+ >
+
+ <include
+ layout="@layout/notification_template_conversation_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+ <include layout="@layout/notification_template_text_multiline" />
+
+ <include
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_progress_bar_height"
+ android:layout_marginTop="@dimen/notification_progress_margin_top"
+ layout="@layout/notification_template_progress"
+ />
+ </LinearLayout>
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/notification_content_margin_end"
+ >
+
+ <include
+ layout="@layout/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+ </FrameLayout>
+
+ </LinearLayout>
+
+ <ViewStub
+ android:layout="@layout/notification_material_reply_text"
+ android:id="@+id/notification_material_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+
+ <include
+ layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginTop="@dimen/notification_content_margin"
+ />
+
+ <include layout="@layout/notification_material_action_list" />
+
+ </LinearLayout>
+
+</com.android.internal.widget.CallLayout>
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index 2954ba2..86e7dec 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -31,7 +31,7 @@
android:layout_height="wrap_content"
android:layout_gravity="top"
android:layout_marginTop="@dimen/notification_content_margin_top"
- android:layout_marginBottom="@dimen/notification_action_list_height"
+ android:layout_marginBottom="@dimen/notification_content_margin"
android:clipToPadding="false"
android:orientation="vertical"
>
diff --git a/core/res/res/layout/notification_template_material_call.xml b/core/res/res/layout/notification_template_material_call.xml
index 7b52ec3..5d9e761 100644
--- a/core/res/res/layout/notification_template_material_call.xml
+++ b/core/res/res/layout/notification_template_material_call.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -19,7 +18,6 @@
android:id="@+id/status_bar_latest_event_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
android:clipChildren="false"
android:tag="call"
android:theme="@style/Theme.DeviceDefault.Notification"
@@ -29,58 +27,46 @@
<include layout="@layout/notification_template_conversation_icon_container" />
<LinearLayout
- android:id="@+id/notification_action_list_margin_target"
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/notification_action_list_height"
- android:orientation="vertical"
+ android:layout_height="80dp"
+ android:orientation="horizontal"
>
<LinearLayout
+ android:id="@+id/notification_main_column"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:layout_gravity="top"
- android:orientation="horizontal"
+ android:layout_marginStart="@dimen/conversation_content_start"
+ android:orientation="vertical"
+ android:minHeight="68dp"
>
- <LinearLayout
- android:id="@+id/notification_main_column"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginStart="@dimen/conversation_content_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
- android:orientation="vertical"
- android:minHeight="68dp"
- >
-
- <include
- layout="@layout/notification_template_conversation_header"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- />
-
- <include layout="@layout/notification_template_text" />
-
- <include
- android:layout_width="match_parent"
- android:layout_height="@dimen/notification_progress_bar_height"
- android:layout_marginTop="@dimen/notification_progress_margin_top"
- layout="@layout/notification_template_progress"
- />
- </LinearLayout>
-
- <!-- TODO(b/179178086): remove padding from main column when this is visible -->
- <include layout="@layout/notification_expand_button"
+ <include
+ layout="@layout/notification_template_conversation_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="top|end"
/>
+ <include layout="@layout/notification_template_text" />
+
</LinearLayout>
- <include layout="@layout/notification_material_action_list" />
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/notification_content_margin_end"
+ >
+
+ <include
+ layout="@layout/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ />
+
+ </FrameLayout>
</LinearLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 6ebd878..62278d5 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3564,6 +3564,7 @@
<attr name="__removed2" format="boolean" />
<!-- Specifies whether the IME supports showing inline suggestions. -->
<attr name="supportsInlineSuggestions" format="boolean" />
+ <attr name="suppressesSpellChecker" format="boolean" />
</declare-styleable>
<!-- This is the subtype of InputMethod. Subtype can describe locales (for example, en_US and
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 40c80db..0979ab55 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3088,6 +3088,7 @@
<public name="selectableAsDefault"/>
<public name="isAccessibilityTool"/>
<public name="attributionTags"/>
+ <public name="suppressesSpellChecker" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8d92c66..2c7f9a4 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5571,6 +5571,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label">On-screen Accessibility Shortcut Chooser</string>
<!-- Label for triggering hardware accessibility shortcut action [CHAR LIMIT=NONE] -->
<string name="accessibility_system_action_hardware_a11y_shortcut_label">Accessibility Shortcut</string>
+ <!-- Label for dismissing the notification shade [CHAR LIMIT=NONE] -->
+ <string name="accessibility_system_action_dismiss_notification_shade">Dismiss Notification Shade</string>
<!-- Accessibility description of caption view -->
<string name="accessibility_freeform_caption">Caption bar of <xliff:g id="app_name">%1$s</xliff:g>.</string>
@@ -5884,9 +5886,9 @@
<!-- Window magnification prompt related string. -->
<!-- Notification title to prompt the user that new magnification feature is available. [CHAR LIMIT=50] -->
- <string name="window_magnification_prompt_title">New: Window Magnifier</string>
- <!-- Notification content to prompt the user that new magnification feature is available. [CHAR LIMIT=50] -->
- <string name="window_magnification_prompt_content">You can now magnify some or all of your screen</string>
+ <string name="window_magnification_prompt_title">Magnify part of your screen</string>
+ <!-- Notification content to prompt the user that new magnification feature is available. [CHAR LIMIT=NONE] -->
+ <string name="window_magnification_prompt_content">You can now magnify your full screen, a specific area, or switch between both options.</string>
<!-- Notification action to bring the user to magnification settings page. [CHAR LIMIT=50] -->
<string name="turn_on_magnification_settings_action">Turn on in Settings</string>
<!-- Notification action to dismiss. [CHAR LIMIT=50] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5a7b1fa..b935788 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3115,6 +3115,7 @@
<!-- Notifications: CallStyle -->
<java-symbol type="layout" name="notification_template_material_call" />
+ <java-symbol type="layout" name="notification_template_material_big_call" />
<java-symbol type="string" name="call_notification_answer_action" />
<java-symbol type="string" name="call_notification_decline_action" />
<java-symbol type="string" name="call_notification_hang_up_action" />
@@ -3986,6 +3987,7 @@
<java-symbol type="string" name="accessibility_system_action_on_screen_a11y_shortcut_label" />
<java-symbol type="string" name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" />
<java-symbol type="string" name="accessibility_system_action_hardware_a11y_shortcut_label" />
+ <java-symbol type="string" name="accessibility_system_action_dismiss_notification_shade" />
<java-symbol type="string" name="accessibility_freeform_caption" />
diff --git a/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java b/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java
index fe31b90..7ef1d5e 100644
--- a/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java
@@ -61,7 +61,7 @@
public void testPutDocument_throwsNullException() throws Exception {
// Create a document
AppSearchEmail inEmail =
- new AppSearchEmail.Builder("uri1")
+ new AppSearchEmail.Builder("namespace", "uri1")
.setFrom("from@example.com")
.setTo("to1@example.com", "to2@example.com")
.setSubject("testPut example")
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java
index 119b70a..ed53d5f 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java
@@ -25,7 +25,7 @@
@Test
public void testBuildEmailAndGetValue() {
AppSearchEmail email =
- new AppSearchEmail.Builder("uri")
+ new AppSearchEmail.Builder("namespace", "uri")
.setFrom("FakeFromAddress")
.setCc("CC1", "CC2")
// Score and Property are mixed into the middle to make sure
@@ -37,6 +37,7 @@
.setBody("EmailBody")
.build();
+ assertThat(email.getNamespace()).isEqualTo("namespace");
assertThat(email.getUri()).isEqualTo("uri");
assertThat(email.getFrom()).isEqualTo("FakeFromAddress");
assertThat(email.getTo()).isNull();
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
index af77b6c..b884ddc 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
@@ -27,13 +27,13 @@
@Test
public void testRecreateFromParcel() {
GenericDocument inDoc =
- new GenericDocument.Builder<>("uri1", "schema1")
+ new GenericDocument.Builder<>("namespace", "uri1", "schema1")
.setScore(42)
.setPropertyString("propString", "Hello")
.setPropertyBytes("propBytes", new byte[][] {{1, 2}})
.setPropertyDocument(
"propDocument",
- new GenericDocument.Builder<>("uri2", "schema2")
+ new GenericDocument.Builder<>("namespace", "uri2", "schema2")
.setPropertyString("propString", "Goodbye")
.setPropertyBytes("propBytes", new byte[][] {{3, 4}})
.build())
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
index 7d175d9..7637214 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java
@@ -31,8 +31,8 @@
public void addGenericDocument_byCollection() {
Set<AppSearchEmail> emails =
ImmutableSet.of(
- new AppSearchEmail.Builder("test1").build(),
- new AppSearchEmail.Builder("test2").build());
+ new AppSearchEmail.Builder("namespace", "test1").build(),
+ new AppSearchEmail.Builder("namespace", "test2").build());
PutDocumentsRequest request =
new PutDocumentsRequest.Builder().addGenericDocuments(emails).build();
diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
index 8de9454..083e37a 100644
--- a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
+++ b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
@@ -46,7 +46,8 @@
private static final String[] USAGESTATS_PERSISTED_FIELDS = {"mBeginTimeStamp", "mEndTimeStamp",
"mPackageName", "mPackageToken", "mLastEvent", "mAppLaunchCount", "mChooserCounts",
"mLastTimeUsed", "mTotalTimeInForeground", "mLastTimeForegroundServiceUsed",
- "mTotalTimeForegroundServiceUsed", "mLastTimeVisible", "mTotalTimeVisible"};
+ "mTotalTimeForegroundServiceUsed", "mLastTimeVisible", "mTotalTimeVisible",
+ "mLastTimeComponentUsed"};
// All fields in this list are defined in UsageStats but not persisted
private static final String[] USAGESTATS_IGNORED_FIELDS = {"CREATOR", "mActivities",
"mForegroundServices", "mLaunchCount", "mChooserCountsObfuscated"};
diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
index 0ac00b8..858bbd2 100644
--- a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
+++ b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
@@ -20,6 +20,7 @@
import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED;
import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED;
+import static android.app.usage.UsageEvents.Event.APP_COMPONENT_USED;
import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE;
import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN;
import static android.app.usage.UsageEvents.Event.END_OF_DAY;
@@ -137,6 +138,7 @@
left.mPackageName = "com.test";
left.mBeginTimeStamp = 100000;
left.mTotalTimeInForeground = 10;
+ left.mLastTimeComponentUsed = 200000;
left.mActivities.put(1, Event.ACTIVITY_RESUMED);
left.mActivities.put(2, Event.ACTIVITY_RESUMED);
@@ -542,6 +544,19 @@
}
@Test
+ public void testEvent_APP_COMPONENT_USED() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+ final String className = "com.test.component1";
+
+ left.update(className, 200000, APP_COMPONENT_USED, 0);
+ assertEquals(left.mLastTimeComponentUsed, 200000);
+
+ left.update(className, 300000, APP_COMPONENT_USED, 0);
+ assertEquals(left.mLastTimeComponentUsed, 300000);
+ }
+
+ @Test
public void testEvent_DEVICE_SHUTDOWN() {
testClosingEvent(DEVICE_SHUTDOWN);
}
@@ -586,6 +601,7 @@
assertEquals(us1.mBeginTimeStamp, us2.mBeginTimeStamp);
assertEquals(us1.mLastTimeUsed, us2.mLastTimeUsed);
assertEquals(us1.mLastTimeVisible, us2.mLastTimeVisible);
+ assertEquals(us1.mLastTimeComponentUsed, us2.mLastTimeComponentUsed);
assertEquals(us1.mLastTimeForegroundServiceUsed, us2.mLastTimeForegroundServiceUsed);
assertEquals(us1.mTotalTimeInForeground, us2.mTotalTimeInForeground);
assertEquals(us1.mTotalTimeForegroundServiceUsed, us2.mTotalTimeForegroundServiceUsed);
diff --git a/core/tests/coretests/src/android/content/ContentProviderTest.java b/core/tests/coretests/src/android/content/ContentProviderTest.java
index 8895f9b..b282064 100644
--- a/core/tests/coretests/src/android/content/ContentProviderTest.java
+++ b/core/tests/coretests/src/android/content/ContentProviderTest.java
@@ -23,6 +23,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ProviderInfo;
import android.net.Uri;
+import android.os.UserHandle;
import androidx.test.runner.AndroidJUnit4;
@@ -86,4 +87,11 @@
mCp.validateIncomingUri(
Uri.parse("content://com.example/foo/bar?foo=b//ar#foo=b//ar")));
}
+
+ @Test
+ public void testCreateContentUriAsUser() {
+ Uri uri = Uri.parse("content://com.example/foo/bar");
+ Uri expectedUri = Uri.parse("content://7@com.example/foo/bar");
+ assertEquals(expectedUri, ContentProvider.createContentUriAsUser(uri, UserHandle.of(7)));
+ }
}
diff --git a/core/tests/coretests/src/android/view/RoundedCornerTest.java b/core/tests/coretests/src/android/view/RoundedCornerTest.java
index 8eb13bc..4349021 100644
--- a/core/tests/coretests/src/android/view/RoundedCornerTest.java
+++ b/core/tests/coretests/src/android/view/RoundedCornerTest.java
@@ -62,6 +62,13 @@
}
@Test
+ public void testIsEmpty_negativeCenter() {
+ RoundedCorner roundedCorner =
+ new RoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT, 1, -2, -3);
+ assertThat(roundedCorner.isEmpty(), is(true));
+ }
+
+ @Test
public void testEquals() {
RoundedCorner roundedCorner = new RoundedCorner(
RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTests.java b/core/tests/mockingcoretests/src/android/view/DisplayTest.java
similarity index 98%
rename from core/tests/mockingcoretests/src/android/view/DisplayTests.java
rename to core/tests/mockingcoretests/src/android/view/DisplayTest.java
index a036db2..f8dd153 100644
--- a/core/tests/mockingcoretests/src/android/view/DisplayTests.java
+++ b/core/tests/mockingcoretests/src/android/view/DisplayTest.java
@@ -56,12 +56,15 @@
*
* <p>Build/Install/Run:
*
- * atest FrameworksMockingCoreTests:android.view.DisplayTests
+ * atest FrameworksMockingCoreTests:android.view.DisplayTest
+ *
+ * <p>This test class is a part of Window Manager Service tests and specified in
+ * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
-public class DisplayTests {
+public class DisplayTest {
private static final int APP_WIDTH = 272;
private static final int APP_HEIGHT = 700;
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index b3a180d..b171599 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -155,6 +155,7 @@
<assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="media" />
<assign-permission name="android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER" uid="media" />
<assign-permission name="android.permission.REGISTER_STATS_PULL_ATOM" uid="media" />
+ <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="media" />
<assign-permission name="android.permission.INTERNET" uid="media" />
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 11cb2b7..7c80f70 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -601,7 +601,7 @@
}
/**
- * Check whether the caller is the credential management app {@link CredentialManagementApp}.
+ * Check whether the caller is the credential management app {@code CredentialManagementApp}.
* The credential management app has the ability to manage the user's KeyChain credentials
* on unmanaged devices.
*
@@ -611,6 +611,7 @@
*
* @return {@code true} if the caller is the credential management app.
*/
+ @WorkerThread
public static boolean isCredentialManagementApp(@NonNull Context context) {
boolean isCredentialManagementApp = false;
try (KeyChainConnection keyChainConnection = KeyChain.bind(context)) {
@@ -634,6 +635,7 @@
* @return the credential management app's authentication policy.
* @throws SecurityException if the caller is not the credential management app.
*/
+ @WorkerThread
@NonNull
public static AppUriAuthenticationPolicy getCredentialManagementAppPolicy(
@NonNull Context context) throws SecurityException {
@@ -665,6 +667,7 @@
* @hide
*/
@TestApi
+ @WorkerThread
@RequiresPermission(Manifest.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP)
public static boolean setCredentialManagementApp(@NonNull Context context,
@NonNull String packageName, @NonNull AppUriAuthenticationPolicy authenticationPolicy) {
@@ -680,13 +683,21 @@
}
/**
- * Remove the user's KeyChain credentials on unmanaged devices.
+ * Called by the credential management app {@code CredentialManagementApp} to unregister as
+ * the credential management app and stop managing the user's credentials.
+ *
+ * <p> All credentials previously installed by the credential management app will be removed
+ * from the user's device.
+ *
+ * <p> An app holding {@code MANAGE_CREDENTIAL_MANAGEMENT_APP} permission can also call this
+ * method to remove the current credential management app, even if it's not the current
+ * credential management app itself.
*
* @return {@code true} if the credential management app was successfully removed.
- * @hide
*/
- @TestApi
- @RequiresPermission(Manifest.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP)
+ @WorkerThread
+ @RequiresPermission(value = Manifest.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP,
+ conditional = true)
public static boolean removeCredentialManagementApp(@NonNull Context context) {
try (KeyChainConnection keyChainConnection = KeyChain.bind(context)) {
keyChainConnection.getService().removeCredentialManagementApp();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
index cb54021..3708e15 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
@@ -119,7 +119,7 @@
listeners.get(i).onDisplayAreaAppeared(displayAreaInfo);
}
}
- applyConfigChangesToContext(displayId, displayAreaInfo.configuration);
+ applyConfigChangesToContext(displayAreaInfo);
}
@Override
@@ -161,24 +161,27 @@
listeners.get(i).onDisplayAreaInfoChanged(displayAreaInfo);
}
}
- applyConfigChangesToContext(displayId, displayAreaInfo.configuration);
+ applyConfigChangesToContext(displayAreaInfo);
}
/**
- * Applies the {@link Configuration} to the {@link DisplayAreaContext} specified by
- * {@code displayId}.
- *
- * @param displayId The ID of the {@link Display} which the {@link DisplayAreaContext} is
- * associated with
- * @param newConfig The propagated configuration
+ * Applies the {@link DisplayAreaInfo} to the {@link DisplayAreaContext} specified by
+ * {@link DisplayAreaInfo#displayId}.
*/
- private void applyConfigChangesToContext(int displayId, @NonNull Configuration newConfig) {
+ private void applyConfigChangesToContext(@NonNull DisplayAreaInfo displayAreaInfo) {
+ final int displayId = displayAreaInfo.displayId;
+ final Display display = mContext.getSystemService(DisplayManager.class)
+ .getDisplay(displayId);
+ if (display == null) {
+ throw new UnsupportedOperationException("The display #" + displayId + " is invalid."
+ + "displayAreaInfo:" + displayAreaInfo);
+ }
DisplayAreaContext daContext = mDisplayAreaContexts.get(displayId);
if (daContext == null) {
- daContext = new DisplayAreaContext(mContext, displayId);
+ daContext = new DisplayAreaContext(mContext, display);
mDisplayAreaContexts.put(displayId, daContext);
}
- daContext.updateConfigurationChanges(newConfig);
+ daContext.updateConfigurationChanges(displayAreaInfo.configuration);
}
/**
@@ -228,10 +231,8 @@
private final IBinder mToken = new Binder();
private final ResourcesManager mResourcesManager = ResourcesManager.getInstance();
- public DisplayAreaContext(@NonNull Context context, int displayId) {
+ public DisplayAreaContext(@NonNull Context context, @NonNull Display display) {
super(null);
- final Display display = context.getSystemService(DisplayManager.class)
- .getDisplay(displayId);
attachBaseContext(context.createTokenContext(mToken, display));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index 562b32b..b6d408a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -23,6 +23,7 @@
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
import android.graphics.Rect;
import android.view.SurfaceControl;
import android.window.WindowContainerToken;
@@ -88,7 +89,8 @@
ProtoLog.v(WM_SHELL_TASK_ORG, "pair task1=%d task2=%d in AppPair=%s",
task1.taskId, task2.taskId, this);
- if (!task1.isResizeable || !task2.isResizeable) {
+ if ((!task1.isResizeable || !task2.isResizeable)
+ && !ActivityTaskManager.supportsNonResizableMultiWindow()) {
ProtoLog.e(WM_SHELL_TASK_ORG,
"Can't pair unresizeable tasks task1.isResizeable=%b task1.isResizeable=%b",
task1.isResizeable, task2.isResizeable);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index 1770943..58bf22a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -85,7 +85,8 @@
@Override
public void onDisplayAdded(int displayId) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Display added: %d", displayId);
- final Context context = mDisplayController.getDisplayContext(displayId);
+ final Context context = mDisplayController.getDisplayContext(displayId)
+ .createWindowContext(TYPE_APPLICATION_OVERLAY, null);
final WindowManager wm = context.getSystemService(WindowManager.class);
// TODO(b/169894807): Figure out the right layer for this, needs to be below the task bar
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 eea5c08..d06064a 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
@@ -30,6 +30,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
import android.app.WindowConfiguration;
import android.graphics.Rect;
import android.os.IBinder;
@@ -91,9 +92,11 @@
// is nothing behind it.
((type == TRANSIT_CLOSE || type == TRANSIT_TO_BACK)
&& triggerTask.parentTaskId == mListener.mPrimary.taskId)
- // if a non-resizable is launched, we also need to leave split-screen.
+ // if a non-resizable is launched when it is not supported in multi window,
+ // we also need to leave split-screen.
|| ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT)
- && !triggerTask.isResizeable);
+ && !triggerTask.isResizeable
+ && !ActivityTaskManager.supportsNonResizableMultiWindow());
// In both cases, dismiss the primary
if (shouldDismiss) {
WindowManagerProxy.buildDismissSplit(out, mListener,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
index 82468ad..5a2ef56 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
@@ -46,6 +46,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.function.BooleanSupplier;
/**
* Proxy to simplify calls into window manager/activity manager
@@ -208,11 +209,17 @@
return false;
}
ActivityManager.RunningTaskInfo topHomeTask = null;
+ // One-time lazy wrapper to avoid duplicated IPC in loop. Not store as class variable
+ // because the value can be changed at runtime.
+ final BooleanSupplier supportsNonResizableMultiWindow =
+ createSupportsNonResizableMultiWindowSupplier();
for (int i = rootTasks.size() - 1; i >= 0; --i) {
final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i);
- // Only move resizeable task to split secondary. However, we have an exception
- // for non-resizable home because we will minimize to show it.
- if (!rootTask.isResizeable && rootTask.topActivityType != ACTIVITY_TYPE_HOME) {
+ // Check whether to move resizeable task to split secondary.
+ // Also, we have an exception for non-resizable home because we will minimize to show
+ // it.
+ if (!rootTask.isResizeable && rootTask.topActivityType != ACTIVITY_TYPE_HOME
+ && !supportsNonResizableMultiWindow.getAsBoolean()) {
continue;
}
// Only move fullscreen tasks to split secondary.
@@ -357,6 +364,21 @@
outWct.setFocusable(tiles.mPrimary.token, true /* focusable */);
}
+ /** Creates a lazy wrapper to get whether it supports non-resizable in multi window. */
+ private static BooleanSupplier createSupportsNonResizableMultiWindowSupplier() {
+ return new BooleanSupplier() {
+ private Boolean mSupportsNonResizableMultiWindow;
+ @Override
+ public boolean getAsBoolean() {
+ if (mSupportsNonResizableMultiWindow == null) {
+ mSupportsNonResizableMultiWindow =
+ ActivityTaskManager.supportsNonResizableMultiWindow();
+ }
+ return mSupportsNonResizableMultiWindow;
+ }
+ };
+ }
+
/**
* Utility to apply a sync transaction serially with other sync transactions.
*
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 5fc7c98..44dad57 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
@@ -164,7 +164,7 @@
new OneHandedBackgroundPanelOrganizer(context, windowManager, displayController,
mainExecutor);
OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
- context, windowManager, displayController, animationController, tutorialHandler,
+ context, windowManager, animationController, tutorialHandler,
oneHandedBackgroundPanelOrganizer, mainExecutor);
OneHandedUiEventLogger oneHandedUiEventsLogger = new OneHandedUiEventLogger(uiEventLogger);
IOverlayManager overlayManager = IOverlayManager.Stub.asInterface(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 0238fa8..5eec231 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -37,7 +37,6 @@
import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.R;
-import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import java.io.PrintWriter;
@@ -67,7 +66,6 @@
private int mEnterExitAnimationDurationMs;
private ArrayMap<WindowContainerToken, SurfaceControl> mDisplayAreaTokenMap = new ArrayMap();
- private DisplayController mDisplayController;
private OneHandedAnimationController mAnimationController;
private OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
@@ -111,7 +109,6 @@
*/
public OneHandedDisplayAreaOrganizer(Context context,
WindowManager windowManager,
- DisplayController displayController,
OneHandedAnimationController animationController,
OneHandedTutorialHandler tutorialHandler,
OneHandedBackgroundPanelOrganizer oneHandedBackgroundGradientOrganizer,
@@ -119,7 +116,6 @@
super(mainExecutor);
mWindowManager = windowManager;
mAnimationController = animationController;
- mDisplayController = displayController;
mLastVisualDisplayBounds.set(getDisplayBounds());
final int animationDurationConfig = context.getResources().getInteger(
R.integer.config_one_handed_translate_animation_duration);
@@ -171,10 +167,14 @@
*/
public void onRotateDisplay(int fromRotation, int toRotation, WindowContainerTransaction wct) {
// Stop one handed without animation and reset cropped size immediately
- final Rect newBounds = new Rect(mDefaultDisplayBounds);
+ final Rect newBounds = new Rect(getDisplayBounds());
+ // This diff rule will only filter the cases portrait <-> landscape
final boolean isOrientationDiff = Math.abs(fromRotation - toRotation) % 2 == 1;
if (isOrientationDiff) {
+ // getDisplayBounds() will return window metrics bounds which dose not update to
+ // corresponding display orientation yet, we have to manual rotate bounds
+ newBounds.set(0, 0, newBounds.bottom, newBounds.right);
resetWindowsOffset(wct);
mDefaultDisplayBounds.set(newBounds);
mLastVisualDisplayBounds.set(newBounds);
@@ -293,7 +293,8 @@
}
@Nullable
- private Rect getDisplayBounds() {
+ @VisibleForTesting
+ Rect getDisplayBounds() {
if (mWindowManager == null) {
Slog.e(TAG, "WindowManager instance is null! Can not get display size!");
return new Rect();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 9ec7c0d..36dc4e4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -41,6 +41,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.PictureInPictureParams;
@@ -423,12 +424,16 @@
if (mInSwipePipToHomeTransition) {
final Rect destinationBounds = mPipBoundsState.getBounds();
+ final SurfaceControl.Transaction tx =
+ mSurfaceControlTransactionFactory.getTransaction();
+ mSurfaceTransactionHelper.resetScale(tx, mLeash, destinationBounds);
+ mSurfaceTransactionHelper.crop(tx, mLeash, destinationBounds);
// animation is finished in the Launcher and here we directly apply the final touch.
applyEnterPipSyncTransaction(destinationBounds, () -> {
// ensure menu's settled in its final bounds first
finishResizeForMenu(destinationBounds);
sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
- });
+ }, tx);
mInSwipePipToHomeTransition = false;
return;
}
@@ -490,16 +495,20 @@
// mState is set right after the animation is kicked off to block any resize
// requests such as offsetPip that may have been called prior to the transition.
mState = State.ENTERING_PIP;
- });
+ }, null /* boundsChangeTransaction */);
}
- private void applyEnterPipSyncTransaction(Rect destinationBounds, Runnable runnable) {
+ private void applyEnterPipSyncTransaction(Rect destinationBounds, Runnable runnable,
+ @Nullable SurfaceControl.Transaction boundsChangeTransaction) {
// PiP menu is attached late in the process here to avoid any artifacts on the leash
// caused by addShellRoot when in gesture navigation mode.
mPipMenuController.attach(mLeash);
final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
wct.setBounds(mToken, destinationBounds);
+ if (boundsChangeTransaction != null) {
+ wct.setBoundsChangeTransaction(mToken, boundsChangeTransaction);
+ }
wct.scheduleFinishEnterPip(mToken, destinationBounds);
mSyncTransactionQueue.queue(wct);
if (runnable != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 543ecfc..44e2624 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -73,6 +73,7 @@
// Allow PIP to resize to a slightly bigger state upon touch
private boolean mEnableResize;
private final Context mContext;
+ private final PipTaskOrganizer mPipTaskOrganizer;
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
private final @NonNull PipBoundsState mPipBoundsState;
private final PipUiEventLogger mPipUiEventLogger;
@@ -169,6 +170,7 @@
mContext = context;
mMainExecutor = mainExecutor;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
+ mPipTaskOrganizer = pipTaskOrganizer;
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipBoundsState = pipBoundsState;
mMenuController = menuController;
@@ -982,7 +984,9 @@
void setPipExclusionBoundsChangeListener(Consumer<Rect> pipExclusionBoundsChangeListener) {
mPipExclusionBoundsChangeListener = new WeakReference<>(pipExclusionBoundsChangeListener);
- pipExclusionBoundsChangeListener.accept(mPipBoundsState.getBounds());
+ pipExclusionBoundsChangeListener.accept(mPipTaskOrganizer.isInPip()
+ ? mPipBoundsState.getBounds() : new Rect());
+
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 14fbaac..50d8098 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -36,6 +36,7 @@
import android.hardware.display.DisplayManager;
import android.os.IBinder;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
@@ -149,11 +150,12 @@
context = displayContext;
if (theme != context.getThemeResId() || labelRes != 0) {
try {
- context = context.createPackageContext(
- activityInfo.packageName, CONTEXT_RESTRICTED);
+ context = context.createPackageContextAsUser(activityInfo.packageName,
+ CONTEXT_RESTRICTED, UserHandle.of(taskInfo.userId));
context.setTheme(theme);
} catch (PackageManager.NameNotFoundException e) {
- // Ignore
+ Slog.w(TAG, "Failed creating package context with package name "
+ + activityInfo.packageName + " for user " + taskInfo.userId, e);
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index 63968f3..98ce274 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -18,6 +18,7 @@
import android.os.SystemClock
import android.platform.test.annotations.Presubmit
+import android.provider.Settings
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -26,6 +27,8 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.wm.shell.flicker.appPairsDividerIsInvisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import org.junit.After
+import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -33,11 +36,10 @@
import org.junit.runners.Parameterized
/**
- * Test AppPairs launch.
- * To run this test: `atest WMShellFlickerTests:AppPairsTest`
- */
-/**
- * Test cold launch app from launcher.
+ * Test cold launch app from launcher. When the device doesn't support non-resizable in multi window
+ * {@link Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW}, app pairs should not pair
+ * non-resizable apps.
+ *
* To run this test: `atest WMShellFlickerTests:AppPairsTestCannotPairNonResizeableApps`
*/
@RequiresDevice
@@ -47,6 +49,7 @@
class AppPairsTestCannotPairNonResizeableApps(
testSpec: FlickerTestParameter
) : AppPairsTransition(testSpec) {
+ var prevSupportNonResizableInMultiWindow = 0
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
@@ -60,6 +63,24 @@
}
}
+ @Before
+ fun setup() {
+ prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
+ if (prevSupportNonResizableInMultiWindow == 1) {
+ // Not support non-resizable in multi window
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0)
+ }
+ }
+
+ @After
+ fun teardown() {
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
+ prevSupportNonResizableInMultiWindow)
+ }
+
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
new file mode 100644
index 0000000..1e3595c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
@@ -0,0 +1,117 @@
+/*
+ * 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.wm.shell.flicker.apppairs
+
+import android.os.SystemClock
+import android.platform.test.annotations.Presubmit
+import android.provider.Settings
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.appPairsDividerIsVisible
+import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import org.junit.After
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test cold launch app from launcher. When the device supports non-resizable in multi window
+ * {@link Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW}, app pairs can pair
+ * non-resizable apps.
+ *
+ * To run this test: `atest WMShellFlickerTests:AppPairsTestSupportPairNonResizeableApps`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class AppPairsTestSupportPairNonResizeableApps(
+ testSpec: FlickerTestParameter
+) : AppPairsTransition(testSpec) {
+ var prevSupportNonResizableInMultiWindow = 0
+
+ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ get() = {
+ super.transition(this, it)
+ transitions {
+ nonResizeableApp?.launchViaIntent(wmHelper)
+ // TODO pair apps through normal UX flow
+ executeShellCommand(
+ composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true))
+ SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ }
+ }
+
+ @Before
+ fun setup() {
+ prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
+ if (prevSupportNonResizableInMultiWindow == 0) {
+ // Support non-resizable in multi window
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1)
+ }
+ }
+
+ @After
+ fun teardown() {
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
+ prevSupportNonResizableInMultiWindow)
+ }
+
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+ @FlakyTest
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
+ @Presubmit
+ @Test
+ fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+
+ @Presubmit
+ @Test
+ fun bothAppWindowVisible() {
+ val nonResizeableApp = nonResizeableApp
+ require(nonResizeableApp != null) {
+ "Non resizeable app not initialized"
+ }
+ testSpec.assertWmEnd {
+ isVisible(nonResizeableApp.defaultWindowName)
+ isVisible(primaryApp.defaultWindowName)
+ }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ repetitions = AppPairsHelper.TEST_REPETITIONS)
+ }
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index 128560a..134d00b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.apppairs
import android.app.Instrumentation
+import android.content.Context
import android.platform.test.annotations.Presubmit
import android.system.helpers.ActivityHelper
import android.util.Log
@@ -46,6 +47,7 @@
abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ protected val context: Context = instrumentation.context
protected val isRotated = testSpec.config.startRotation.isRotated()
protected val activityHelper = ActivityHelper.getInstance()
protected val appPairsHelper = AppPairsHelper(instrumentation,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
index afaf33a..c7a1c9a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
@@ -16,7 +16,7 @@
package com.android.wm.shell.flicker.pip
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -51,40 +51,40 @@
}
}
- @Postsubmit
+ @Presubmit
@Test
override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible()
- @Postsubmit
+ @Presubmit
@Test
override fun statusBarLayerIsAlwaysVisible() = super.statusBarLayerIsAlwaysVisible()
- @Postsubmit
+ @Presubmit
@Test
override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
- @Postsubmit
+ @Presubmit
@Test
override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
- @Postsubmit
+ @Presubmit
@Test
override fun pipWindowBecomesInvisible() = super.pipWindowBecomesInvisible()
- @Postsubmit
+ @Presubmit
@Test
override fun pipLayerBecomesInvisible() = super.pipLayerBecomesInvisible()
- @Postsubmit
+ @Presubmit
@Test
override fun statusBarLayerRotatesScales() =
testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- @Postsubmit
+ @Presubmit
@Test
override fun noUncoveredRegions() = super.noUncoveredRegions()
- @Postsubmit
+ @Presubmit
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index 3309e10..bf148bc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -16,7 +16,7 @@
package com.android.wm.shell.flicker.pip
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -78,7 +78,7 @@
}
}
- @Postsubmit
+ @Presubmit
@Test
fun pipWindowInsideDisplayBounds() {
testSpec.assertWm {
@@ -86,7 +86,7 @@
}
}
- @Postsubmit
+ @Presubmit
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
@@ -96,15 +96,15 @@
}
}
- @Postsubmit
+ @Presubmit
@Test
override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
- @Postsubmit
+ @Presubmit
@Test
override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
- @Postsubmit
+ @Presubmit
@Test
fun pipLayerInsideDisplayBounds() {
testSpec.assertLayers {
@@ -112,7 +112,7 @@
}
}
- @Postsubmit
+ @Presubmit
@Test
fun bothAppLayersVisible() {
testSpec.assertLayersEnd {
@@ -121,11 +121,11 @@
}
}
- @Postsubmit
+ @Presubmit
@Test
override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible()
- @Postsubmit
+ @Presubmit
@Test
override fun statusBarLayerIsAlwaysVisible() = super.statusBarLayerIsAlwaysVisible()
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 c5221de..b21276f 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
@@ -121,8 +121,8 @@
final OneHandedAnimationController animationController = new OneHandedAnimationController(
mContext);
OneHandedDisplayAreaOrganizer displayAreaOrganizer = new OneHandedDisplayAreaOrganizer(
- mContext, mWindowManager, mMockDisplayController, animationController,
- mMockTutorialHandler, mMockBackgroundOrganizer, mMockShellMainExecutor);
+ mContext, mWindowManager, animationController, mMockTutorialHandler,
+ mMockBackgroundOrganizer, mMockShellMainExecutor);
assertThat(displayAreaOrganizer.isInOneHanded()).isFalse();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
index 1fa1e2f..f897b09 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -122,7 +122,6 @@
mSpiedDisplayAreaOrganizer = spy(new OneHandedDisplayAreaOrganizer(mContext,
mWindowManager,
- mMockDisplayController,
mMockAnimationController,
mTutorialHandler,
mMockBackgroundOrganizer,
@@ -170,6 +169,15 @@
}
@Test
+ public void testRotation_getNewDisplayBounds() {
+ when(mMockLeash.isValid()).thenReturn(false);
+ // Rotate 0 -> 90
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_90,
+ mMockWindowContainerTransaction);
+ verify(mSpiedDisplayAreaOrganizer).getDisplayBounds();
+ }
+
+ @Test
public void testRotation_portrait_0_to_landscape_90() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 0 -> 90
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index e93824d..0112686 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -336,6 +336,7 @@
GET_DEV_PROC(ResetCommandBuffer);
GET_DEV_PROC(ResetFences);
GET_DEV_PROC(WaitForFences);
+ GET_DEV_PROC(FrameBoundaryANDROID);
}
void VulkanManager::initialize() {
@@ -516,6 +517,25 @@
if (semaphore != VK_NULL_HANDLE) {
if (submitted == GrSemaphoresSubmitted::kYes) {
mSwapSemaphore = semaphore;
+ if (mFrameBoundaryANDROID) {
+ // retrieve VkImage used as render target
+ VkImage image = VK_NULL_HANDLE;
+ GrBackendRenderTarget backendRenderTarget =
+ surface->getBackendRenderTarget(SkSurface::kFlushRead_BackendHandleAccess);
+ if (backendRenderTarget.isValid()) {
+ GrVkImageInfo info;
+ if (backendRenderTarget.getVkImageInfo(&info)) {
+ image = info.fImage;
+ } else {
+ ALOGE("Frame boundary: backend is not vulkan");
+ }
+ } else {
+ ALOGE("Frame boundary: invalid backend render target");
+ }
+ // frameBoundaryANDROID needs to know about mSwapSemaphore, but
+ // it won't wait on it.
+ mFrameBoundaryANDROID(mDevice, mSwapSemaphore, image);
+ }
} else {
destroy_semaphore(mDestroySemaphoreContext);
mDestroySemaphoreContext = nullptr;
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 0912369..7b5fe19 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -31,6 +31,21 @@
#include <vk/GrVkExtensions.h>
#include <vulkan/vulkan.h>
+// VK_ANDROID_frame_boundary is a bespoke extension defined by AGI
+// (https://github.com/google/agi) to enable profiling of apps rendering via
+// HWUI. This extension is not defined in Khronos, hence the need to declare it
+// manually here. There's a superseding extension (VK_EXT_frame_boundary) being
+// discussed in Khronos, but in the meantime we use the bespoke
+// VK_ANDROID_frame_boundary. This is a device extension that is implemented by
+// AGI's Vulkan capture layer, such that it is only supported by devices when
+// AGI is doing a capture of the app.
+//
+// TODO(b/182165045): use the Khronos blessed VK_EXT_frame_boudary once it has
+// landed in the spec.
+typedef void(VKAPI_PTR* PFN_vkFrameBoundaryANDROID)(VkDevice device, VkSemaphore semaphore,
+ VkImage image);
+#define VK_ANDROID_FRAME_BOUNDARY_EXTENSION_NAME "VK_ANDROID_frame_boundary"
+
#include "Frame.h"
#include "IRenderPipeline.h"
#include "VulkanSurface.h"
@@ -160,6 +175,7 @@
VkPtr<PFN_vkDestroyFence> mDestroyFence;
VkPtr<PFN_vkWaitForFences> mWaitForFences;
VkPtr<PFN_vkResetFences> mResetFences;
+ VkPtr<PFN_vkFrameBoundaryANDROID> mFrameBoundaryANDROID;
VkInstance mInstance = VK_NULL_HANDLE;
VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 548b415..ae64c02 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -21,7 +21,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
-import android.annotation.TestApi;
import android.app.ActivityThread;
import android.app.Application;
import android.compat.annotation.UnsupportedAppUsage;
@@ -2937,9 +2936,7 @@
* @return a {@link PlaybackComponent} associated with the session,
* or {@code null} if the session is closed or does not exist.
* @see PlaybackComponent
- * @hide
*/
- @TestApi
@Nullable
public PlaybackComponent getPlaybackComponent(@NonNull byte[] sessionId) {
if (sessionId == null) {
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index b4db305..9566e1c 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -21,6 +21,7 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -89,6 +90,7 @@
private final CopyOnWriteArrayList<ControllerCreationRequest> mControllerCreationRequests =
new CopyOnWriteArrayList<>();
+ // TODO: Specify the fields that are only used (or not used) by system media router.
private final String mClientPackageName;
private final ManagerCallback mManagerCallback;
@@ -132,18 +134,34 @@
}
/**
- * Gets an instance of the media router which controls the app's media routing.
+ * Gets an instance of the system media router which controls the app's media routing.
* Returns {@code null} if the given package name is invalid.
+ * There are several things to note when using the media routers created with this method.
* <p>
- * Note: For media routers created with this method, the discovery preference passed to
- * {@link #registerRouteCallback} will have no effect. The callback will be called accordingly
- * with the client app's discovery preference. Therefore, it is recommended to pass
+ * First of all, the discovery preference passed to {@link #registerRouteCallback}
+ * will have no effect. The callback will be called accordingly with the client app's
+ * discovery preference. Therefore, it is recommended to pass
* {@link RouteDiscoveryPreference#EMPTY} there.
+ * <p>
+ * Also, do not keep/compare the instances of the {@link RoutingController}, since they are
+ * always newly created with the latest session information whenever below methods are called:
+ * <ul>
+ * <li> {@link #getControllers()} </li>
+ * <li> {@link #getController(String)}} </li>
+ * <li> {@link TransferCallback#onTransfer(RoutingController, RoutingController)} </li>
+ * <li> {@link TransferCallback#onStop(RoutingController)} </li>
+ * <li> {@link ControllerCallback#onControllerUpdated(RoutingController)} </li>
+ * </ul>
+ * Therefore, in order to track the current routing status, keep the controller's ID instead,
+ * and use {@link #getController(String)} and {@link #getSystemController()} for
+ * getting controllers.
+ * <p>
+ * Finally, it will have no effect to call {@link #setOnGetControllerHintsListener}.
*
* @param clientPackageName the package name of the app to control
* @hide
*/
- //@SystemApi
+ @SystemApi
@Nullable
public static MediaRouter2 getInstance(@NonNull Context context,
@NonNull String clientPackageName) {
@@ -168,12 +186,40 @@
instance = new MediaRouter2(context, clientPackageName);
sSystemMediaRouter2Map.put(clientPackageName, instance);
// TODO: Remove router instance once it is not needed.
- instance.registerManagerCallback();
+ instance.registerManagerCallbackForSystemRouter();
}
return instance;
}
}
+ /**
+ * Starts scanning remote routes.
+ * Note that calling start/stopScan is applied to all system routers in the same process.
+ *
+ * @see #stopScan()
+ * @hide
+ */
+ @SystemApi
+ public void startScan() {
+ if (isSystemRouter()) {
+ sManager.startScan();
+ }
+ }
+
+ /**
+ * Stops scanning remote routes to reduce resource consumption.
+ * Note that calling start/stopScan is applied to all system routers in the same process.
+ *
+ * @see #startScan()
+ * @hide
+ */
+ @SystemApi
+ public void stopScan() {
+ if (isSystemRouter()) {
+ sManager.stopScan();
+ }
+ }
+
private MediaRouter2(Context appContext) {
mContext = appContext;
mMediaRouterService = IMediaRouterService.Stub.asInterface(
@@ -209,13 +255,18 @@
}
private MediaRouter2(Context context, String clientPackageName) {
+ mContext = context;
mClientPackageName = clientPackageName;
mManagerCallback = new ManagerCallback();
- mContext = context;
- mMediaRouterService = null;
- mPackageName = null;
mHandler = new Handler(Looper.getMainLooper());
- mSystemController = null;
+ mSystemController = new SystemRoutingController(sManager.getSystemRoutingSession());
+ mDiscoveryPreference = new RouteDiscoveryPreference.Builder(
+ sManager.getPreferredFeatures(clientPackageName), true).build();
+ updateAllRoutesFromManager();
+ mMediaRouterService = null; // TODO: Make this non-null and check permission.
+
+ // Only used by non-system MediaRouter2.
+ mPackageName = null;
}
/**
@@ -240,7 +291,7 @@
* @see #getInstance(Context, String)
* @hide
*/
- //@SystemApi
+ @SystemApi
@Nullable
public String getClientPackageName() {
return mClientPackageName;
@@ -358,7 +409,8 @@
*
* @hide
*/
- //@SystemApi
+ @SystemApi
+ @NonNull
public List<MediaRoute2Info> getAllRoutes() {
if (isSystemRouter()) {
return sManager.getAllRoutes();
@@ -377,10 +429,6 @@
*/
@NonNull
public List<MediaRoute2Info> getRoutes() {
- if (isSystemRouter()) {
- return sManager.getAvailableRoutes(mClientPackageName);
- }
-
synchronized (mLock) {
if (mShouldUpdateRoutes) {
mShouldUpdateRoutes = false;
@@ -474,6 +522,9 @@
* {@code null} for unset.
*/
public void setOnGetControllerHintsListener(@Nullable OnGetControllerHintsListener listener) {
+ if (isSystemRouter()) {
+ return;
+ }
mOnGetControllerHintsListener = listener;
}
@@ -519,7 +570,7 @@
* @param route the route you want to transfer the media to.
* @hide
*/
- //@SystemApi
+ @SystemApi
public void transfer(@NonNull RoutingController controller, @NonNull MediaRoute2Info route) {
if (isSystemRouter()) {
sManager.transfer(controller.getRoutingSessionInfo(), route);
@@ -606,6 +657,23 @@
}
/**
+ * Gets a {@link RoutingController} whose ID is equal to the given ID.
+ * Returns {@code null} if there is no matching controller.
+ * @hide
+ */
+ @SystemApi
+ @Nullable
+ public RoutingController getController(@NonNull String id) {
+ Objects.requireNonNull(id, "id must not be null");
+ for (RoutingController controller : getControllers()) {
+ if (TextUtils.equals(id, controller.getId())) {
+ return controller;
+ }
+ }
+ return null;
+ }
+
+ /**
* Gets the list of currently active {@link RoutingController routing controllers} on which
* media can be played.
* <p>
@@ -614,15 +682,25 @@
*/
@NonNull
public List<RoutingController> getControllers() {
- // TODO: Do not create the controller instances every time,
- // Instead, update the list using the sessions' ID and session related callbacks.
+ List<RoutingController> result = new ArrayList<>();
+
if (isSystemRouter()) {
- return sManager.getRoutingSessions(mClientPackageName).stream()
- .map(info -> new RoutingController(info))
- .collect(Collectors.toList());
+ // Unlike non-system MediaRouter2, controller instances cannot be kept,
+ // since the transfer events initiated from other apps will not come through manager.
+ List<RoutingSessionInfo> sessions = sManager.getRoutingSessions(mClientPackageName);
+ for (RoutingSessionInfo session : sessions) {
+ RoutingController controller;
+ if (session.isSystemSession()) {
+ mSystemController.setRoutingSessionInfo(session);
+ controller = mSystemController;
+ } else {
+ controller = new RoutingController(session);
+ }
+ result.add(controller);
+ }
+ return result;
}
- List<RoutingController> result = new ArrayList<>();
result.add(0, mSystemController);
synchronized (mLock) {
result.addAll(mNonSystemRoutingControllers.values());
@@ -639,9 +717,15 @@
* @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}.
* @hide
*/
+ @SystemApi
public void setRouteVolume(@NonNull MediaRoute2Info route, int volume) {
Objects.requireNonNull(route, "route must not be null");
+ if (isSystemRouter()) {
+ sManager.setRouteVolume(route, volume);
+ return;
+ }
+
MediaRouter2Stub stub;
synchronized (mLock) {
stub = mStub;
@@ -928,8 +1012,9 @@
/**
* Registers {@link MediaRouter2Manager.Callback} for getting events.
+ * Should only used for system media routers.
*/
- private void registerManagerCallback() {
+ private void registerManagerCallbackForSystemRouter() {
// Using direct executor here, since MediaRouter2Manager also posts to the main handler.
sManager.registerCallback(Runnable::run, mManagerCallback);
}
@@ -941,6 +1026,16 @@
.collect(Collectors.toList());
}
+ private void updateAllRoutesFromManager() {
+ synchronized (mLock) {
+ mRoutes.clear();
+ for (MediaRoute2Info route : sManager.getAllRoutes()) {
+ mRoutes.put(route.getId(), route);
+ }
+ mShouldUpdateRoutes = true;
+ }
+ }
+
private void notifyRoutesAdded(List<MediaRoute2Info> routes) {
for (RouteCallbackRecord record: mRouteCallbackRecords) {
List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mPreference);
@@ -971,6 +1066,13 @@
}
}
+ private void notifyPreferredFeaturesChanged(List<String> features) {
+ for (RouteCallbackRecord record: mRouteCallbackRecords) {
+ record.mExecutor.execute(
+ () -> record.mRouteCallback.onPreferredFeaturesChanged(features));
+ }
+ }
+
private void notifyTransfer(RoutingController oldController, RoutingController newController) {
for (TransferCallbackRecord record: mTransferCallbackRecords) {
record.mExecutor.execute(
@@ -1024,6 +1126,17 @@
* @param routes the list of routes that have been changed. It's never empty.
*/
public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {}
+
+ /**
+ * Called when the client app's preferred features are changed.
+ * When this is called, it is recommended to {@link #getRoutes()} to get the routes
+ * that are currently available to the app.
+ *
+ * @param preferredFeatures the new preferred features set by the application
+ * @hide
+ */
+ @SystemApi
+ public void onPreferredFeaturesChanged(@NonNull List<String> preferredFeatures) {}
}
/**
@@ -1131,6 +1244,11 @@
mState = CONTROLLER_STATE_ACTIVE;
}
+ RoutingController(@NonNull RoutingSessionInfo sessionInfo, int state) {
+ mSessionInfo = sessionInfo;
+ mState = state;
+ }
+
/**
* @return the ID of the controller. It is globally unique.
*/
@@ -1291,6 +1409,11 @@
return;
}
+ if (isSystemRouter()) {
+ sManager.selectRoute(getRoutingSessionInfo(), route);
+ return;
+ }
+
MediaRouter2Stub stub;
synchronized (mLock) {
stub = mStub;
@@ -1338,6 +1461,11 @@
return;
}
+ if (isSystemRouter()) {
+ sManager.deselectRoute(getRoutingSessionInfo(), route);
+ return;
+ }
+
MediaRouter2Stub stub;
synchronized (mLock) {
stub = mStub;
@@ -1407,6 +1535,12 @@
Log.w(TAG, "setVolume: Called on released controller. Ignoring.");
return;
}
+
+ if (isSystemRouter()) {
+ sManager.setSessionVolume(getRoutingSessionInfo(), volume);
+ return;
+ }
+
MediaRouter2Stub stub;
synchronized (mLock) {
stub = mStub;
@@ -1471,6 +1605,11 @@
mState = CONTROLLER_STATE_RELEASED;
}
+ if (isSystemRouter()) {
+ sManager.releaseSession(getRoutingSessionInfo());
+ return;
+ }
+
synchronized (mLock) {
mNonSystemRoutingControllers.remove(getId(), this);
@@ -1539,6 +1678,12 @@
}
private List<MediaRoute2Info> getRoutesWithIds(List<String> routeIds) {
+ if (isSystemRouter()) {
+ return getRoutes().stream()
+ .filter(r -> routeIds.contains(r.getId()))
+ .collect(Collectors.toList());
+ }
+
synchronized (mLock) {
return routeIds.stream().map(mRoutes::get)
.filter(Objects::nonNull)
@@ -1722,12 +1867,17 @@
}
}
+ // Note: All methods are run on main thread.
class ManagerCallback implements MediaRouter2Manager.Callback {
@Override
public void onRoutesAdded(@NonNull List<MediaRoute2Info> routes) {
- List<MediaRoute2Info> filteredRoutes =
- sManager.filterRoutesForPackage(routes, mClientPackageName);
+ updateAllRoutesFromManager();
+
+ List<MediaRoute2Info> filteredRoutes;
+ synchronized (mLock) {
+ filteredRoutes = filterRoutes(routes, mDiscoveryPreference);
+ }
if (filteredRoutes.isEmpty()) {
return;
}
@@ -1739,8 +1889,12 @@
@Override
public void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) {
- List<MediaRoute2Info> filteredRoutes =
- sManager.filterRoutesForPackage(routes, mClientPackageName);
+ updateAllRoutesFromManager();
+
+ List<MediaRoute2Info> filteredRoutes;
+ synchronized (mLock) {
+ filteredRoutes = filterRoutes(routes, mDiscoveryPreference);
+ }
if (filteredRoutes.isEmpty()) {
return;
}
@@ -1752,8 +1906,12 @@
@Override
public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {
- List<MediaRoute2Info> filteredRoutes =
- sManager.filterRoutesForPackage(routes, mClientPackageName);
+ updateAllRoutesFromManager();
+
+ List<MediaRoute2Info> filteredRoutes;
+ synchronized (mLock) {
+ filteredRoutes = filterRoutes(routes, mDiscoveryPreference);
+ }
if (filteredRoutes.isEmpty()) {
return;
}
@@ -1764,31 +1922,98 @@
}
@Override
- public void onSessionUpdated(@NonNull RoutingSessionInfo session) {
- // TODO: Call ControllerCallback.onControllerUpdated
- }
-
- @Override
public void onTransferred(@NonNull RoutingSessionInfo oldSession,
- @Nullable RoutingSessionInfo newSession) {
- // TODO: Call TransferCallback.onTransfer
+ @NonNull RoutingSessionInfo newSession) {
+ if (!oldSession.isSystemSession()
+ && !TextUtils.equals(mClientPackageName, oldSession.getClientPackageName())) {
+ return;
+ }
+
+ if (!newSession.isSystemSession()
+ && !TextUtils.equals(mClientPackageName, newSession.getClientPackageName())) {
+ return;
+ }
+
+ // For successful in-session transfer, onControllerUpdated() handles it.
+ if (TextUtils.equals(oldSession.getId(), newSession.getId())) {
+ return;
+ }
+
+
+ RoutingController oldController;
+ if (oldSession.isSystemSession()) {
+ mSystemController.setRoutingSessionInfo(oldSession);
+ oldController = mSystemController;
+ } else {
+ oldController = new RoutingController(oldSession);
+ }
+
+ RoutingController newController;
+ if (newSession.isSystemSession()) {
+ mSystemController.setRoutingSessionInfo(newSession);
+ newController = mSystemController;
+ } else {
+ newController = new RoutingController(newSession);
+ }
+
+ notifyTransfer(oldController, newController);
}
@Override
public void onTransferFailed(@NonNull RoutingSessionInfo session,
@NonNull MediaRoute2Info route) {
- // TODO: Call TransferCallback.onTransferFailure
+ if (!session.isSystemSession()
+ && !TextUtils.equals(mClientPackageName, session.getClientPackageName())) {
+ return;
+ }
+ notifyTransferFailure(route);
+ }
+
+ @Override
+ public void onSessionUpdated(@NonNull RoutingSessionInfo session) {
+ if (!session.isSystemSession()
+ && !TextUtils.equals(mClientPackageName, session.getClientPackageName())) {
+ return;
+ }
+
+ RoutingController controller;
+ if (session.isSystemSession()) {
+ mSystemController.setRoutingSessionInfo(session);
+ controller = mSystemController;
+ } else {
+ controller = new RoutingController(session);
+ }
+ notifyControllerUpdated(controller);
}
@Override
public void onSessionReleased(@NonNull RoutingSessionInfo session) {
- // TODO: Call TransferCallback.onStop()
+ if (session.isSystemSession()) {
+ Log.e(TAG, "onSessionReleased: Called on system session. Ignoring.");
+ return;
+ }
+
+ if (!TextUtils.equals(mClientPackageName, session.getClientPackageName())) {
+ return;
+ }
+
+ notifyStop(new RoutingController(session, RoutingController.CONTROLLER_STATE_RELEASED));
}
@Override
public void onPreferredFeaturesChanged(@NonNull String packageName,
@NonNull List<String> preferredFeatures) {
- // Does nothing.
+ if (!TextUtils.equals(mClientPackageName, packageName)) {
+ return;
+ }
+
+ synchronized (mLock) {
+ mDiscoveryPreference = new RouteDiscoveryPreference.Builder(
+ preferredFeatures, true).build();
+ }
+
+ updateAllRoutesFromManager();
+ notifyPreferredFeaturesChanged(preferredFeatures);
}
@Override
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index ca619d4..6fefbe1 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -148,7 +148,7 @@
/**
* Starts scanning remote routes.
- * @see #stopScan(String)
+ * @see #stopScan()
*/
public void startScan() {
Client client = getOrCreateClient();
@@ -163,7 +163,7 @@
/**
* Stops scanning remote routes to reduce resource consumption.
- * @see #startScan(String)
+ * @see #startScan()
*/
public void stopScan() {
Client client = getOrCreateClient();
@@ -237,6 +237,20 @@
}
/**
+ * Returns the preferred features of the specified package name.
+ */
+ @NonNull
+ public List<String> getPreferredFeatures(@NonNull String packageName) {
+ Objects.requireNonNull(packageName, "packageName must not be null");
+
+ List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName);
+ if (preferredFeatures == null) {
+ preferredFeatures = Collections.emptyList();
+ }
+ return preferredFeatures;
+ }
+
+ /**
* Returns a list of routes which are related to the given package name in the given route list.
*/
@NonNull
@@ -788,8 +802,8 @@
* Requests releasing a session.
* <p>
* If a session is released, any operation on the session will be ignored.
- * {@link Callback#onTransferred(RoutingSessionInfo, RoutingSessionInfo)} with {@code null}
- * session will be called when the session is released.
+ * {@link Callback#onSessionReleased(RoutingSessionInfo)} will be called
+ * when the session is released.
* </p>
*
* @see Callback#onTransferred(RoutingSessionInfo, RoutingSessionInfo)
@@ -945,10 +959,10 @@
* Called when media is transferred.
*
* @param oldSession the previous session
- * @param newSession the new session or {@code null} if the session is released.
+ * @param newSession the new session
*/
default void onTransferred(@NonNull RoutingSessionInfo oldSession,
- @Nullable RoutingSessionInfo newSession) { }
+ @NonNull RoutingSessionInfo newSession) { }
/**
* Called when {@link #transfer(RoutingSessionInfo, MediaRoute2Info)} fails.
diff --git a/media/jni/tuner/DvrClient.cpp b/media/jni/tuner/DvrClient.cpp
index 7793180..0476216 100644
--- a/media/jni/tuner/DvrClient.cpp
+++ b/media/jni/tuner/DvrClient.cpp
@@ -314,6 +314,11 @@
}
Result DvrClient::close() {
+ if (mDvrMQEventFlag != NULL) {
+ EventFlag::deleteEventFlag(&mDvrMQEventFlag);
+ }
+ mDvrMQ = NULL;
+
if (mTunerDvr != NULL) {
Status s = mTunerDvr->close();
mTunerDvr = NULL;
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index f31d465..8846e4d6 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -259,6 +259,11 @@
}
Result FilterClient::close() {
+ if (mFilterMQEventFlag != NULL) {
+ EventFlag::deleteEventFlag(&mFilterMQEventFlag);
+ }
+ mFilterMQ = NULL;
+
if (mTunerFilter != NULL) {
Status s = mTunerFilter->close();
closeAvSharedMemory();
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index 4b33366..a045714 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -8,9 +8,13 @@
public class ConnectivityManager {
method @NonNull @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public java.util.List<android.net.NetworkStateSnapshot> getAllNetworkStateSnapshot();
method @NonNull public static android.util.Range<java.lang.Integer> getIpSecNetIdRange();
+ method @NonNull public static String getPrivateDnsMode(@NonNull android.content.ContentResolver);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
+ field public static final String PRIVATE_DNS_MODE_OFF = "off";
+ field public static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
+ field public static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = "hostname";
}
public final class NetworkAgentConfig implements android.os.Parcelable {
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index 7189be1..66c45ed 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -23,6 +23,8 @@
import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT;
import static android.net.QosCallback.QosCallbackRegistrationException;
+import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE;
+import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
@@ -31,11 +33,13 @@
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.StringDef;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.PendingIntent;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.net.IpSecManager.UdpEncapsulationSocket;
@@ -63,6 +67,7 @@
import android.provider.Settings;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Range;
@@ -802,24 +807,27 @@
/**
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public static final String PRIVATE_DNS_MODE_OFF = "off";
/**
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
/**
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = "hostname";
- /**
- * The default Private DNS mode.
- *
- * This may change from release to release or may become dependent upon
- * the capabilities of the underlying platform.
- *
- * @hide
- */
- public static final String PRIVATE_DNS_DEFAULT_MODE_FALLBACK = PRIVATE_DNS_MODE_OPPORTUNISTIC;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(value = {
+ PRIVATE_DNS_MODE_OFF,
+ PRIVATE_DNS_MODE_OPPORTUNISTIC,
+ PRIVATE_DNS_MODE_PROVIDER_HOSTNAME,
+ })
+ public @interface PrivateDnsMode {}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
private final IConnectivityManager mService;
@@ -5128,4 +5136,24 @@
public static Range<Integer> getIpSecNetIdRange() {
return new Range(TUN_INTF_NETID_START, TUN_INTF_NETID_START + TUN_INTF_NETID_RANGE - 1);
}
+
+ /**
+ * Get private DNS mode from settings.
+ *
+ * @param cr The ContentResolver to query private DNS mode from settings.
+ * @return A string of private DNS mode as one of the PRIVATE_DNS_MODE_* constants.
+ *
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @NonNull
+ @PrivateDnsMode
+ public static String getPrivateDnsMode(@NonNull ContentResolver cr) {
+ String mode = Settings.Global.getString(cr, PRIVATE_DNS_MODE);
+ if (TextUtils.isEmpty(mode)) mode = Settings.Global.getString(cr, PRIVATE_DNS_DEFAULT_MODE);
+ // If both PRIVATE_DNS_MODE and PRIVATE_DNS_DEFAULT_MODE are not set, choose
+ // PRIVATE_DNS_MODE_OPPORTUNISTIC as default mode.
+ if (TextUtils.isEmpty(mode)) mode = PRIVATE_DNS_MODE_OPPORTUNISTIC;
+ return mode;
+ }
}
diff --git a/packages/Connectivity/framework/src/android/net/IpPrefix.java b/packages/Connectivity/framework/src/android/net/IpPrefix.java
index d2ee7d1..bf4481a 100644
--- a/packages/Connectivity/framework/src/android/net/IpPrefix.java
+++ b/packages/Connectivity/framework/src/android/net/IpPrefix.java
@@ -113,7 +113,7 @@
// first statement in constructor". We could factor out setting the member variables to an
// init() method, but if we did, then we'd have to make the members non-final, or "error:
// cannot assign a value to final variable address". So we just duplicate the code here.
- Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(prefix);
+ Pair<InetAddress, Integer> ipAndMask = NetworkUtils.legacyParseIpAndMask(prefix);
this.address = ipAndMask.first.getAddress();
this.prefixLength = ipAndMask.second;
checkAndMaskAddressAndPrefixLength();
diff --git a/packages/Connectivity/framework/src/android/net/LinkAddress.java b/packages/Connectivity/framework/src/android/net/LinkAddress.java
index d1bdaa0..d48b8c7 100644
--- a/packages/Connectivity/framework/src/android/net/LinkAddress.java
+++ b/packages/Connectivity/framework/src/android/net/LinkAddress.java
@@ -325,7 +325,7 @@
public LinkAddress(@NonNull String address, int flags, int scope) {
// This may throw an IllegalArgumentException; catching it is the caller's responsibility.
// TODO: consider rejecting mapped IPv4 addresses such as "::ffff:192.0.2.5/24".
- Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(address);
+ Pair<InetAddress, Integer> ipAndMask = NetworkUtils.legacyParseIpAndMask(address);
init(ipAndMask.first, ipAndMask.second, flags, scope, LIFETIME_UNKNOWN, LIFETIME_UNKNOWN);
}
diff --git a/packages/Connectivity/framework/src/android/net/NetworkUtils.java b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
index 9e42bbe..c0f2628 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkUtils.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
@@ -27,8 +27,10 @@
import java.io.FileDescriptor;
import java.math.BigInteger;
import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.SocketException;
+import java.net.UnknownHostException;
import java.util.Locale;
import java.util.TreeSet;
@@ -212,7 +214,7 @@
@Deprecated
public static InetAddress numericToInetAddress(String addrString)
throws IllegalArgumentException {
- return InetAddress.parseNumericAddress(addrString);
+ return InetAddresses.parseNumericAddress(addrString);
}
/**
@@ -234,7 +236,7 @@
try {
String[] pieces = ipAndMaskString.split("/", 2);
prefixLength = Integer.parseInt(pieces[1]);
- address = InetAddress.parseNumericAddress(pieces[0]);
+ address = InetAddresses.parseNumericAddress(pieces[0]);
} catch (NullPointerException e) { // Null string.
} catch (ArrayIndexOutOfBoundsException e) { // No prefix length.
} catch (NumberFormatException e) { // Non-numeric prefix.
@@ -249,6 +251,47 @@
}
/**
+ * Utility method to parse strings such as "192.0.2.5/24" or "2001:db8::cafe:d00d/64".
+ * @hide
+ *
+ * @deprecated This method is used only for IpPrefix and LinkAddress. Since Android S, use
+ * {@link #parseIpAndMask(String)}, if possible.
+ */
+ @Deprecated
+ public static Pair<InetAddress, Integer> legacyParseIpAndMask(String ipAndMaskString) {
+ InetAddress address = null;
+ int prefixLength = -1;
+ try {
+ String[] pieces = ipAndMaskString.split("/", 2);
+ prefixLength = Integer.parseInt(pieces[1]);
+ if (pieces[0] == null || pieces[0].isEmpty()) {
+ final byte[] bytes = new byte[16];
+ bytes[15] = 1;
+ return new Pair<InetAddress, Integer>(Inet6Address.getByAddress(
+ "ip6-localhost"/* host */, bytes, 0 /* scope_id */), prefixLength);
+ }
+
+ if (pieces[0].startsWith("[")
+ && pieces[0].endsWith("]")
+ && pieces[0].indexOf(':') != -1) {
+ pieces[0] = pieces[0].substring(1, pieces[0].length() - 1);
+ }
+ address = InetAddresses.parseNumericAddress(pieces[0]);
+ } catch (NullPointerException e) { // Null string.
+ } catch (ArrayIndexOutOfBoundsException e) { // No prefix length.
+ } catch (NumberFormatException e) { // Non-numeric prefix.
+ } catch (IllegalArgumentException e) { // Invalid IP address.
+ } catch (UnknownHostException e) { // IP address length is illegal
+ }
+
+ if (address == null || prefixLength == -1) {
+ throw new IllegalArgumentException("Invalid IP address and mask " + ipAndMaskString);
+ }
+
+ return new Pair<InetAddress, Integer>(address, prefixLength);
+ }
+
+ /**
* Convert a 32 char hex string into a Inet6Address.
* throws a runtime exception if the string isn't 32 chars, isn't hex or can't be
* made into an Inet6Address
diff --git a/packages/Connectivity/framework/src/android/net/ProxyInfo.java b/packages/Connectivity/framework/src/android/net/ProxyInfo.java
index 229db0d..745e20f 100644
--- a/packages/Connectivity/framework/src/android/net/ProxyInfo.java
+++ b/packages/Connectivity/framework/src/android/net/ProxyInfo.java
@@ -129,7 +129,7 @@
}
/**
- * Only used in PacProxyInstaller after Local Proxy is bound.
+ * Only used in PacProxyService after Local Proxy is bound.
* @hide
*/
public ProxyInfo(@NonNull Uri pacFileUrl, int localProxyPort) {
diff --git a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
index 340141b..c510079 100644
--- a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
+++ b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
@@ -42,9 +42,9 @@
MessageUtils.findMessageNames(new Class[]{VpnManager.class}, new String[]{"TYPE_VPN_"});
/** Type of this VPN. */
- @VpnManager.VpnType public final int type;
+ public final int type;
- public VpnTransportInfo(@VpnManager.VpnType int type) {
+ public VpnTransportInfo(int type) {
this.type = type;
}
diff --git a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java
index 781bfcd..1ccf417 100644
--- a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java
+++ b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java
@@ -51,6 +51,7 @@
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
+ setSingleLineTitle(true);
super.onBindViewHolder(holder);
final View switchView = holder.findViewById(android.R.id.switch_widget);
if (switchView != null) {
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 1a67b5e..0ee44f8 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (rooi-groen)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (blou-geel)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Kleurregstelling"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Verstel hoe kleure op jou toestel vertoon. Dit kan nuttig wees wanneer jy:<br/><br/> <ol> <li> Kleure meer akkuraat wil sien</li> <li> Kleure wil verwyder om jou te help fokus</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Geneutraliseer deur <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Ongeveer <xliff:g id="TIME_REMAINING">%1$s</xliff:g> oor"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Totdat jy dit afskakel"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Sopas"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Foonluidspreker"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Hierdie foon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Kan nie koppel nie. Skakel toestel af en weer aan"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Bedrade oudiotoestel"</string>
<string name="help_label" msgid="3528360748637781274">"Hulp en terugvoer"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 97abce1..9fb676f 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"ፕሮታኖማሊ (ቀይ-አረንጓዴ)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ትራይታኖማሊ (ሰማያዊ-ቢጫ)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"የቀለም ማስተካከያ"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"ቀለሞች በመሣሪያዎ ላይ እንዴት እንደሚታዩ ያስተካክሉ። የሚከተሉትን ለማድረግ በሚፈልጉበት ጊዜ ይህ ጠቃሚ ሊሆን ይችላል፦<br/><br/> <ol> <li> ቀለሞችን የበለጠ ትክክለኛ በሆነ መልኩ ለመመልከት</li> <li> ትኩረት ለማድረግ እንዲረዳዎ ቀለሞችን ለማስወገድ</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"በ<xliff:g id="TITLE">%1$s</xliff:g> ተሽሯል"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ገደማ ቀርቷል"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"እስኪያጠፉት ድረስ"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ልክ አሁን"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"የስልክ ድምጽ ማጉያ"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"ይህ ስልክ"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"መገናኘት ላይ ችግር። መሳሪያውን ያጥፉት እና እንደገና ያብሩት"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ባለገመድ የኦዲዮ መሣሪያ"</string>
<string name="help_label" msgid="3528360748637781274">"እገዛ እና ግብረመልስ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index f9e12c7..cdb418a 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"غطش الأحمر (الأحمر والأخضر)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"غمش الأزرق (الأزرق والأصفر)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"تصحيح الألوان"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"يمكنك تعديل كيفية عرض الألوان على جهازك. يساعدك هذا الخيار عندما تريد تنفيذ ما يلي:<br/><br/> <ol> <li> عرض الألوان بمزيد من الدقة</li> <li> إزالة الألوان لمساعدتك على التركيز</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"تم الاستبدال بـ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"يتبقى <xliff:g id="TIME_REMAINING">%1$s</xliff:g> تقريبًا"</string>
@@ -520,8 +519,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"إلى أن يتم إيقاف الوضع"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"للتو"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"مكبر صوت الهاتف"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"هذا الهاتف"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"حدثت مشكلة أثناء الاتصال. يُرجى إيقاف الجهاز ثم إعادة تشغيله."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"جهاز سماعي سلكي"</string>
<string name="help_label" msgid="3528360748637781274">"المساعدة والملاحظات والآراء"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index efd813a..442a19d 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"প্ৰ’টানোমালি (ৰঙা-সেউজীয়া)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ট্ৰাইটান\'মেলী (নীলা-হালধীয়া)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ৰং শুধৰণী"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"আপোনাৰ ডিভাইচত ৰংবোৰ কেনেকৈ প্ৰদৰ্শিত হয় সেয়া মিলাওক। এইটো আপুনি এই কাৰ্য কৰিবলৈ বিচাৰিলে সহায়ক হ\'ব পাৰে:<br/><br/> <ol> <li> ৰং অধিক সঠিককৈ চাবলৈ বিচৰা</li> <li> আপোনাক মনোযোগ দিয়াত সহায় কৰিবলৈ ৰং আঁতৰোৱা</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g>ৰ দ্বাৰা অগ্ৰাহ্য কৰা হৈছে"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"প্রায় <xliff:g id="TIME_REMAINING">%1$s</xliff:g> বাকী আছে"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"আপুনি অফ নকৰা পর্যন্ত"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"এই মাত্ৰ"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ফ’নৰ স্পীকাৰ"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"এই ফ’নটো"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"সংযোগ হোৱাত সমস্যা হৈছে। ডিভাইচটো অফ কৰি পুনৰ অন কৰক"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"তাঁৰযুক্ত অডিঅ’ ডিভাইচ"</string>
<string name="help_label" msgid="3528360748637781274">"সহায় আৰু মতামত"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 7a5aec4..950a5ae 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Пратанамалія (чырвоны-зялёны)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Трытанамалія (сіні-жоўты)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Карэкцыя колеру"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Наладзьце адлюстраванне колераў на экране прылады. Гэта налада можа быць карыснай, калі вы захочаце:<br/><br/> <ol> <li> бачыць колеры больш дакладна;</li> <li> выдаліць колеры, якія перашкаджаюць вам сканцэнтравацца</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Перавызначаны <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Зараду хопіць прыблізна на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Пакуль не выключыце"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Толькі што"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Дынамік тэлефона"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Гэты тэлефон"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Праблема з падключэннем. Выключыце і зноў уключыце прыладу"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Правадная аўдыяпрылада"</string>
<string name="help_label" msgid="3528360748637781274">"Даведка і водгукі"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 675d179..4cc9abf 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"প্রোটানোম্যালি (লাল-সবুজ)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ট্রিট্যানোম্যালি (নীল-হলুদ)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"রঙ সংশোধন"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"আপনার ডিভাইসে রঙগুলি কেমন দেখাবে তা অ্যাডজাস্ট করুন। যেক্ষেত্রে এটি আপনাকে সহায়তা করতে পারে:<br/><br/> <ol> <li> আরও নির্ভুলভাবে রঙ দেখতে</li> <li> রঙ সরিয়ে দিলে ফোকাস করতে সহায়তা করবে</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> এর দ্বারা ওভাররাইড করা হয়েছে"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"আর আনুমানিক <xliff:g id="TIME_REMAINING">%1$s</xliff:g> চলবে"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"যতক্ষণ না আপনি বন্ধ করছেন"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"এখনই"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ফোনের স্পিকার"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"এই ফোনটি"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"কানেক্ট করতে সমস্যা হচ্ছে। ডিভাইস বন্ধ করে আবার চালু করুন"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ওয়্যার অডিও ডিভাইস"</string>
<string name="help_label" msgid="3528360748637781274">"সহায়তা ও মতামত"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 97083ad..e4bc5a1 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalija (crveno-zeleno)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalija (plavo-žuto)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Ispravka boje"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Podešavanje načina na koji se boje prikazuju na uređaju. To može biti korisno kada želite:<br/><br/> <ol> <li> tačnije prikazati boje</li> <li> ukloniti boje da se lakše fokusirate</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Zamjenjuje <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Preostalo je još oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -517,8 +516,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Dok ne isključite"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Upravo"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Zvučnik telefona"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Došlo je do problema prilikom povezivanja. Isključite, pa ponovo uključite uređaj"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audio uređaj"</string>
<string name="help_label" msgid="3528360748637781274">"Pomoć i povratne informacije"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 4889220..fa441a2 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalia (vermell-verd)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalia (blau-groc)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correcció de color"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajusta com es mostren els colors al teu dispositiu. Això pot ser útil quan vulguis:<br/><br/> <ol> <li> Veure els colors amb més claredat.</li> <li> Suprimir colors per poder enfocar més fàcilment.</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"S\'ha substituït per <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>: <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Temps restant aproximat: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Fins que no el desactivis"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Ara mateix"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altaveu del telèfon"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Aquest telèfon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Hi ha hagut un problema amb la connexió. Desactiva el dispositiu i torna\'l a activar."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositiu d\'àudio amb cable"</string>
<string name="help_label" msgid="3528360748637781274">"Ajuda i suggeriments"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index e857e60..717b681 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomálie (červená a zelená)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomálie (modrá a žlutá)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Korekce barev"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Upravte zobrazování barev na svém zařízení. To se může hodit, když chcete:<br/><br/> <ol> <li> Aby se barvy zobrazovaly přesněji</li> <li> Odstranit barvy, abyste se mohli lépe soustředit</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Přepsáno nastavením <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Zbývá asi <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Dokud tuto funkci nevypnete"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Právě teď"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Reproduktor telefonu"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tento telefon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problém s připojením. Vypněte zařízení a znovu jej zapněte"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kabelové audiozařízení"</string>
<string name="help_label" msgid="3528360748637781274">"Nápověda a zpětná vazba"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 53e21ba..6b17db1 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanopi (rød-grøn)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanopi (blå-gul)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Korriger farver"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Juster, hvordan farverne vises på skærmen. Dette kan være nyttigt, når du vil:<br/><br/> <ol> <li> Se farver mere nøjagtigt</li> <li> Fjerne farver, så du bedre kan fokusere</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Tilsidesat af <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Ca. <xliff:g id="TIME_REMAINING">%1$s</xliff:g> tilbage"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Indtil du deaktiverer"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Lige nu"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefonens højttaler"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Denne telefon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Der kunne ikke oprettes forbindelse. Sluk og tænd enheden"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Lydenhed med ledning"</string>
<string name="help_label" msgid="3528360748637781274">"Hjælp og feedback"</string>
@@ -541,7 +539,7 @@
<string name="user_add_profile_item_title" msgid="3111051717414643029">"Begrænset profil"</string>
<string name="user_add_user_title" msgid="5457079143694924885">"Vil du tilføje en ny bruger?"</string>
<string name="user_add_user_message_long" msgid="1527434966294733380">"Du kan dele denne enhed med andre ved at oprette ekstra brugere. Hver bruger har sit personlige område, som kan tilpasses med apps, baggrund osv. Brugerne kan også justere enhedsindstillinger, som for eksempel Wi-Fi, som påvirker alle.\n\nNår du tilføjer en ny bruger, skal vedkommende konfigurere sit område.\n\nAlle brugere kan opdatere apps for alle andre brugere. Indstillinger og tjenester for hjælpefunktioner overføres muligvis ikke til den nye bruger."</string>
- <string name="user_add_user_message_short" msgid="3295959985795716166">"Når du tilføjer en ny bruger, skal personen konfigurere sit rum.\n\nEnhver bruger kan opdatere apps for alle andre brugere."</string>
+ <string name="user_add_user_message_short" msgid="3295959985795716166">"Når du tilføjer en ny bruger, skal personen konfigurere sit rum.\n\nAlle brugere kan opdatere apps for alle de andre brugere."</string>
<string name="user_setup_dialog_title" msgid="8037342066381939995">"Vil du konfigurere brugeren nu?"</string>
<string name="user_setup_dialog_message" msgid="269931619868102841">"Sørg for, at brugeren har mulighed for at tage enheden og konfigurere sit eget rum"</string>
<string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"Vil du oprette en profil nu?"</string>
@@ -557,7 +555,7 @@
<string name="user_switch_to_user" msgid="6975428297154968543">"Skift til <xliff:g id="USER_NAME">%s</xliff:g>"</string>
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Opretter ny bruger…"</string>
<string name="user_nickname" msgid="262624187455825083">"Kaldenavn"</string>
- <string name="guest_new_guest" msgid="3482026122932643557">"Tilføj gæsten"</string>
+ <string name="guest_new_guest" msgid="3482026122932643557">"Tilføj gæst"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gæsten"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gæst"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Tag et billede"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index a4a976f..bb11ed1 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (Rot-Grün-Sehschwäche)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (Blau-Gelb-Sehschwäche)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Farbkorrektur"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Hier kannst du anpassen, wie Farben auf deinem Gerät dargestellt werden sollen. Das kann in folgenden Fällen hilfreich sein:<br/><br/> <ol> <li> Wenn Farben genauer dargestellt werden sollen</li> <li> Wenn du Farben entfernen möchtest, um dich besser konzentrieren zu können</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Außer Kraft gesetzt von \"<xliff:g id="TITLE">%1$s</xliff:g>\""</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Noch etwa <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Bis zur Deaktivierung"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Gerade eben"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Smartphone-Lautsprecher"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Dieses Smartphone"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Verbindung kann nicht hergestellt werden. Schalte das Gerät aus & und wieder ein."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Netzbetriebenes Audiogerät"</string>
<string name="help_label" msgid="3528360748637781274">"Hilfe und Feedback"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 71cf5cb..fb294c8 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalía (rojo-verde)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalía (azul-amarillo)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Corrección de color"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajusta cómo se muestran los colores en tu dispositivo. Esto puede ser útil cuando quieres:<br/><br/> <ol> <li> Ver colores con más exactitud</li> <li> Quitar colores para mejorar tu concentración</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Reemplazado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Tiempo restante: aproximadamente <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Hasta que lo desactives"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Recién"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altavoz del teléfono"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Error al establecer la conexión. Apaga el dispositivo y vuelve a encenderlo."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string>
<string name="help_label" msgid="3528360748637781274">"Ayuda y comentarios"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index a7d7b21..2c5eff2 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalía (rojo-verde)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalía (azul-amarillo)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Corrección de color"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajusta el modo en que se muestran los colores en tu dispositivo. Esto puede ser útil cuando quieras hacer lo siguiente:<br/><br/> <ol> <li> Ver los colores con más precisión</li> <li> Eliminar colores para ayudarte a mantener la concentración</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>: <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Tiempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Hasta que lo desactives"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"justo ahora"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altavoz del teléfono"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"No se ha podido conectar; reinicia el dispositivo"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string>
<string name="help_label" msgid="3528360748637781274">"Ayuda y comentarios"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index add25f8..116faad 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomaalia (punane-roheline)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaalia (sinine-kollane)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Värvide korrigeerimine"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Kohandage seadmes värvide kuvamist. Sellest võib olla kasu, kui soovite:<br/><br/> <ol> <li> värve täpsemalt näha;</li> <li> värve eemaldada, et paremini keskenduda.</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Alistas <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Ligikaudu <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäänud"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Kuni välja lülitate"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Äsja"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefoni kõlar"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"See telefon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Probleem ühendamisel. Lülitage seade välja ja uuesti sisse"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Juhtmega heliseade"</string>
<string name="help_label" msgid="3528360748637781274">"Abi ja tagasiside"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index c95d157..bfb4efc 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanopia (gorri-berdeak)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanopia (urdin-horia)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Koloreen zuzenketa"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Doitu nola bistaratzen diren koloreak gailuan. Kasu hauetan izan daiteke lagungarria:<br/><br/> <ol> <li> Koloreak zehatzago ikusi nahi dituzunean.</li> <li> Hobeto fokuratzeko, koloreak kendu nahi dituzunean.</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> hobespena gainjarri zaio"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> inguru gelditzen dira"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Zuk desaktibatu arte"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Oraintxe"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefonoaren bozgorailua"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Telefono hau"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Arazoren bat izan da konektatzean. Itzali gailua eta pitz ezazu berriro."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Audio-gailu kableduna"</string>
<string name="help_label" msgid="3528360748637781274">"Laguntza eta iritziak"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 631ff5e..f3018ba 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"قرمزدشواربینی (قرمز-سبز)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"آبیدشواربینی (آبی-زرد)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"تصحیح رنگ"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"نحوه نمایش رنگها را در دستگاهتان تنظیم میکند. این ویژگی میتواند در موارد زیر مفید باشد:<br/><br/> <ol> <li> وقتی میخواهید رنگها را با دقت بیشتری ببینید</li> <li> وقتی میخواهید رنگها را حذف کنید تا تمرکز بیشتری داشته باشید"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"توسط <xliff:g id="TITLE">%1$s</xliff:g> لغو شد"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"تقریباً <xliff:g id="TIME_REMAINING">%1$s</xliff:g> شارژ باقی مانده است"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"تا زمانیکه آن را خاموش کنید"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"هماکنون"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"بلندگوی تلفن"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"این تلفن"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"مشکل در اتصال. دستگاه را خاموش و دوباره روشن کنید"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"دستگاه صوتی سیمی"</string>
<string name="help_label" msgid="3528360748637781274">"راهنما و بازخورد"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 7591c91..7089937 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalia (puna-vihersokeus)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalia (sini-keltasokeus)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Värinkorjaus"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Muuta värien näkymistä laitteellasi. Tästä voi olla hyötyä, kun haluat<br/><br/> <ol> <li> nähdä värit tarkemmin</li> <li> poistaa värejä voidaksesi keskittyä paremmin</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Tämän ohittaa <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Noin <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäljellä"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Kunnes laitat pois päältä"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Äsken"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Puhelimen kaiutin"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tämä puhelin"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Yhteysvirhe. Sammuta laite ja käynnistä se uudelleen."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Langallinen äänilaite"</string>
<string name="help_label" msgid="3528360748637781274">"Ohje ja palaute"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index a390257..ac8adde 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (rouge/vert)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (bleu/jaune)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correction des couleurs"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajustez l\'affichage des couleurs sur votre appareil. Ce paramètre peut être utile si vous voulez :<br/><br/> <ol> <li> Mieux distinguer les couleurs</li> <li> Enlever les couleurs pour vous aider à vous concentrer</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> : <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Il reste environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Jusqu\'à la désactivation"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"À l\'instant"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Haut-parleur du téléphone"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ce téléphone"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problème de connexion. Éteingez et rallumez l\'appareil"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Appareil audio à câble"</string>
<string name="help_label" msgid="3528360748637781274">"Aide et commentaires"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 8cf8c6e..e4e4293 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (rouge/vert)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (bleu-jaune)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correction des couleurs"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajustez l\'affichage des couleurs sur votre appareil. Cette option peut vous être utile pour :<br/><br/> <ol> <li> accentuer la précision des couleurs ;</li> <li> supprimer les couleurs pour mieux vous concentrer.</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Temps restant : environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Jusqu\'à la désactivation"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"À l\'instant"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Haut-parleur du téléphone"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ce téléphone"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problème de connexion. Éteignez l\'appareil, puis rallumez-le"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Appareil audio filaire"</string>
<string name="help_label" msgid="3528360748637781274">"Aide et commentaires"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index c1ba51d..062b7b3 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalía (vermello-verde)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalía (azul-amarelo)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Corrección da cor"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Axusta a maneira en que se mostran as cores no teu dispositivo. Esta opción pode resultarche útil se queres:<br/><br/> <ol> <li> Ver mellor as cores</li> <li> Quitar as cores para concentrarte mellor</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Tempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Ata a desactivación"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Agora mesmo"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altofalante do teléfono"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Produciuse un problema coa conexión. Apaga e acende o dispositivo."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string>
<string name="help_label" msgid="3528360748637781274">"Axuda e comentarios"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 94d83e0..174d0a1 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"પ્રોટેનોમલી (લાલ-લીલો)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ટ્રાઇટેનોમલી(વાદળી-પીળો)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"રંગ સુધારણા"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"તમારા ડિવાઇસ પર રંગો કેવી રીતે બતાવવામાં આવે તેની ગોઠવણી કરો. આ ત્યારે સહાયરૂપ થઈ શકે છે જ્યારે તમારે:<br/><br/> <ol> <li> રંગો વધુ યોગ્ય રીતે જોવા હોય</li> <li> તમને ફોકસ કરવામાં સહાયતા રહે તે માટે રંગો કાઢી નાખવા હોય</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> દ્વારા ઓવરરાઇડ થયું"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"લગભગ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> બાકી છે"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"તમે બંધ ન કરો ત્યાં સુધી"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"હમણાં જ"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ફોન સ્પીકર"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"આ ફોન"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"કનેક્ટ કરવામાં સમસ્યા આવી રહી છે. ડિવાઇસને બંધ કરીને ફરી ચાલુ કરો"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"વાયરવાળો ઑડિયો ડિવાઇસ"</string>
<string name="help_label" msgid="3528360748637781274">"સહાય અને પ્રતિસાદ"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index e99d4cb..3d06baa 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalija (crveno – zeleno)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalija (plavo – žuto)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Korekcija boje"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Prilagodite način prikazivanja boja na svojem uređaju. To može biti korisno kad želite:<br/><br/> <ol> <li> vidjeti boje točnije</li> <li> ukloniti boje kako biste se lakše usredotočili</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Premošćeno postavkom <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Još otprilike <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -517,8 +516,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Dok ne isključite"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Upravo sad"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Zvučnik telefona"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem s povezivanjem. Isključite i ponovo uključite uređaj"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audiouređaj"</string>
<string name="help_label" msgid="3528360748637781274">"Pomoć i povratne informacije"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 503ee60..546a038 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomália (piros– zöld)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomália (kék–sárga)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Színkorrekció"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Korrigálhatja a színek megjelenítését az eszközén. Ez a következő esetekben lehet hasznos:<br/><br/> <ol> <li> ha pontosabb színeket szeretne látni;</li> <li> ha szeretné eltávolítani a színeket, hogy jobban tudjon koncentrálni.</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Felülírva erre: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Körülbelül <xliff:g id="TIME_REMAINING">%1$s</xliff:g> maradt hátra"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Kikapcsolásig"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Az imént"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefon hangszórója"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ez a telefon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Sikertelen csatlakozás. Kapcsolja ki az eszközt, majd kapcsolja be újra."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Vezetékes audioeszköz"</string>
<string name="help_label" msgid="3528360748637781274">"Súgó és visszajelzés"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 3686dd1..7c0963d 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Պրոտանոմալիա (կարմիր-կանաչ)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Տրիտանոմալիա (կապույտ-դեղին)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Գունաշտկում"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Կարգավորեք գույների ցուցադրումը ձեր սարքում։ Դա կարող է օգտակար լինել, երբ դուք ուզում եք՝<br/><br/> <ol> <li> Ավելի հստակ տեսնել գույները</li> <li> Հեռացնել գույները՝ կենտրոնանալու համար</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Գերազանցված է <xliff:g id="TITLE">%1$s</xliff:g>-ից"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Լիցքը կբավարարի մոտ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Մինչև չանջատեք"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Հենց նոր"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Հեռախոսի բարձրախոս"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Այս հեռախոսը"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Կապի խնդիր կա: Սարքն անջատեք և նորից միացրեք:"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Լարով աուդիո սարք"</string>
<string name="help_label" msgid="3528360748637781274">"Օգնություն և հետադարձ կապ"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 035be7d..2f8bb46 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomali (merah-hijau)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomali (biru-kuning)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Koreksi warna"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Sesuaikan cara warna ditampilkan di perangkat Anda. Ini dapat bermanfaat saat Anda ingin:<br/><br/> <ol> <li> Melihat warna dengan lebih akurat</li> <li> Menghapus warna untuk membantu Anda fokus</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Digantikan oleh <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Sekitar <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Sampai Anda menonaktifkannya"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Baru saja"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Speaker ponsel"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ponsel ini"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ada masalah saat menghubungkan. Nonaktifkan perangkat & aktifkan kembali"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Perangkat audio berkabel"</string>
<string name="help_label" msgid="3528360748637781274">"Bantuan & masukan"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 718b5be..ea29258 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Litblinda (rauðgræn)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Litblinda (blágul)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Litaleiðrétting"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Stilltu litabirtingu í tækinu þínu. Þetta getur gagnast þegar þú vilt:<br/><br/> <ol> <li> Sjá liti skýrar</li> <li> Fjarlægja liti til að fókusa betur</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Hnekkt af <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Um það bil <xliff:g id="TIME_REMAINING">%1$s</xliff:g> eftir"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Þar til þú slekkur"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Rétt í þessu"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Símahátalari"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Þessi sími"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Vandamál í tengingu. Slökktu og kveiktu á tækinu"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Snúrutengt hljómtæki"</string>
<string name="help_label" msgid="3528360748637781274">"Hjálp og ábendingar"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index d2176d9..5dd7a52 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalìa (rosso-verde)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalìa (blu-giallo)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correzione del colore"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Regola la modalità di visualizzazione dei colori sul tuo dispositivo. Può essere utile se vuoi:<br/><br/> <ol> <li> Vedere i colori con più precisione</li> <li> Rimuovere colori per mettere a fuoco più facilmente</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Valore sostituito da <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Tempo rimanente: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> circa"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Fino alla disattivazione"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Adesso"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altoparlante telefono"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Questo telefono"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problema di connessione. Spegni e riaccendi il dispositivo"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo audio cablato"</string>
<string name="help_label" msgid="3528360748637781274">"Guida e feedback"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 1357cae..20d1e1b 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"פרוטנומליה (אדום-ירוק)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"טריטנומליה (כחול-צהוב)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"תיקון צבע"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"ניתן לשנות את האופן שבו צבעים מוצגים במכשיר. שינוי כזה עשוי לעזור:<br/><br/> <ol> <li> להבחין בצבעים בצורה יותר מדויקת</li> <li> להסיר צבעים מסוימים כדי להתמקד</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"נעקף על ידי <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"הזמן הנותר: בערך <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"עד הכיבוי"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"הרגע"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"רמקול של טלפון"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"הטלפון הזה"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"יש בעיה בחיבור. עליך לכבות את המכשיר ולהפעיל אותו מחדש"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"התקן אודיו חוטי"</string>
<string name="help_label" msgid="3528360748637781274">"עזרה ומשוב"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 8bb6ba8..37db15c 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"პროტოანომალია (წითელი-მწვანე)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ტრიტანომალია (ლურჯი-ყვითელი)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ფერის კორექცია"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"დააკორექტირეთ, როგორ გამოჩნდება ფერები თქვენს მოწყობილობაზე. ეს შეიძლება დაგეხმაროთ, როდესაც გსურთ:<br/><br/> <ol> <li> ფერების მეტი სიზუსტით დანახვა</li> <li> ფერების მოცილება, რომ უკეთ კონცენტრირდეთ</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"უკუგებულია <xliff:g id="TITLE">%1$s</xliff:g>-ის მიერ"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"დარჩა დაახლოებით <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"გამორთვამდე"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ახლახან"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ტელეფონის დინამიკი"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"ეს ტელეფონი"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"დაკავშირებისას წარმოიქმნა პრობლემა. გამორთეთ და კვლავ ჩართეთ მოწყობილობა"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"სადენიანი აუდიო მოწყობილობა"</string>
<string name="help_label" msgid="3528360748637781274">"დახმარება და გამოხმაურება"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 6a2c21e..878966d 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномалия (қызыл-жасыл)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомалия (көк-сары)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Түсті түзету"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Құрылғыңызда түстер қалай көрсетілетінін реттеңіз. Бұл мыналар үшін пайдалы болуы мүмкін:<br/><br/> <ol> <li> түстерді анығырақ көру</li> <li> зейініңізді жақсарту үшін түстерді өшіру</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> үстінен басқан"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Шамамен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> қалды"</string>
@@ -503,7 +502,7 @@
<string name="cancel" msgid="5665114069455378395">"Бас тарту"</string>
<string name="okay" msgid="949938843324579502">"Жарайды"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Қосу"</string>
- <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\"Мазаламау\" режимін қосу"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Мазаламау режимін қосу"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Ешқашан"</string>
<string name="zen_interruption_level_priority" msgid="5392140786447823299">"Маңыздылары ғана"</string>
<string name="zen_mode_and_condition" msgid="8877086090066332516">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Өшірілгенге дейін"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Дәл қазір"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Телефон динамигі"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Осы телефон"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Байланыс орнату қатесі шығуып жатыр. Құрылғыны өшіріп, қайта қосыңыз."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Сымды аудио құрылғысы"</string>
<string name="help_label" msgid="3528360748637781274">"Анықтама және пікір"</string>
@@ -557,8 +555,8 @@
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> пайдаланушысына ауысу"</string>
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Жаңа пайдаланушы профилі жасалуда…"</string>
<string name="user_nickname" msgid="262624187455825083">"Лақап ат"</string>
- <string name="guest_new_guest" msgid="3482026122932643557">"Қонақты енгізу"</string>
- <string name="guest_exit_guest" msgid="5908239569510734136">"Қонақты өшіру"</string>
+ <string name="guest_new_guest" msgid="3482026122932643557">"Қонақ қосу"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Қонақты жою"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Қонақ"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Фотосуретке түсіру"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Сурет таңдау"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index f4cece4..708879a 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -556,7 +556,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"កំពុងបង្កើតអ្នកប្រើប្រាស់ថ្មី…"</string>
<string name="user_nickname" msgid="262624187455825083">"ឈ្មោះហៅក្រៅ"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"បញ្ចូលភ្ញៀវ"</string>
- <string name="guest_exit_guest" msgid="5908239569510734136">"លុបភ្ញៀវ"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"ដកភ្ញៀវចេញ"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ភ្ញៀវ"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"ថតរូប"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"ជ្រើសរើសរូបភាព"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index a844ee6..daf7b6f 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"ಪ್ರೊಟನೋಮಲಿ (ಕೆಂಪು-ಹಸಿರು)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ಟ್ರಿಟನೋಮಲಿ (ನೀಲಿ-ಹಳದಿ)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ಬಣ್ಣದ ತಿದ್ದುಪಡಿ"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಬಣ್ಣಗಳು ಹೇಗೆ ಡಿಸ್ಪ್ಲೇ ಆಗುತ್ತವೆ ಎಂಬುದನ್ನು ಹೊಂದಿಸಿ. ನೀವು ಬಣ್ಣಗಳನ್ನು ಹೆಚ್ಚು ನಿಖರವಾಗಿ ನೋಡಲು ಬಯಸಿದಾಗ:<br/><br/> <ol> <li> ಇದು ಸಹಾಯಕವಾಗಿರುತ್ತದೆ</li> <li> ನಿಮಗೆ ಗಮನಹರಿಸಲು ಸಹಾಯ ಮಾಡಲು ಬಣ್ಣಗಳನ್ನು ತೆಗೆದುಹಾಕಿ</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ಮೂಲಕ ಅತಿಕ್ರಮಿಸುತ್ತದೆ"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ಸಮಯ ಬಾಕಿ ಉಳಿದಿದೆ"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"ನೀವು ಆಫ್ ಮಾಡುವವರೆಗೆ"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ಇದೀಗ"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ಫೋನ್ ಸ್ಪೀಕರ್"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"ಈ ಫೋನ್"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ಕನೆಕ್ಟ್ ಮಾಡುವಾಗ ಸಮಸ್ಯೆ ಎದುರಾಗಿದೆ ಸಾಧನವನ್ನು ಆಫ್ ಮಾಡಿ ಹಾಗೂ ನಂತರ ಪುನಃ ಆನ್ ಮಾಡಿ"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ವೈರ್ ಹೊಂದಿರುವ ಆಡಿಯೋ ಸಾಧನ"</string>
<string name="help_label" msgid="3528360748637781274">"ಸಹಾಯ ಮತ್ತು ಪ್ರತಿಕ್ರಿಯೆ"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 4ca6567..834200d 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномалия (кызыл-жашыл)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомалия (көк-сары)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Түсүн тууралоо"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Түзмөгүңүздө түстөр кантип көрүнөрүн тууралаңыз. Бул төмөнкү учурларда пайдалуу болот:<br/><br/> <ol> <li> Түстөрдү даана көрүү</li> <li> Ынтаа коюу үчүн түстөрдү өчүрүү</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> менен алмаштырылган"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Болжол менен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> калды"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Бул функция өчүрүлгөнгө чейин"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Жаңы эле"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Телефондун динамиги"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ушул телефон"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Туташууда маселе келип чыкты. Түзмөктү өчүрүп, кайра күйгүзүп көрүңүз"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Зымдуу аудио түзмөк"</string>
<string name="help_label" msgid="3528360748637781274">"Жардам/Пикир билдирүү"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 176c80f..0dcf1627 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomaly (ສີແດງ-ສີຂຽວ)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaly (ສີຟ້າ-ສີເຫຼືອງ)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ການປັບແຕ່ງສີ"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"ປັບແກ້ການສະແດງສີຢູ່ອຸປະກອນຂອງທ່ານ. ນີ້ອາດມີປະໂຫຍດໃນເວລາທີ່ທ່ານຕ້ອງການ:<br/><br/> <ol> <li> ເບິ່ງເຫັນສີໄດ້ຖືກຕ້ອງຍິ່ງຂຶ້ນ</li> <li> ລຶບສີອອກເພື່ອຊ່ວຍໃຫ້ທ່ານມີສະມາທິ</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"ຖືກແທນໂດຍ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"ເຫຼືອອີກປະມານ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"ຈົນກວ່າທ່ານຈະປິດ"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ຕອນນີ້"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ລຳໂພງໂທລະສັບ"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"ໂທລະສັບນີ້"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ເກີດບັນຫາໃນການເຊື່ອມຕໍ່. ປິດອຸປະກອນແລ້ວເປີດກັບຄືນມາໃໝ່"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ອຸປະກອນສຽງແບບມີສາຍ"</string>
<string name="help_label" msgid="3528360748637781274">"ຊ່ວຍເຫຼືອ ແລະ ຕິຊົມ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index b0ab8f4..c3f9202 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalija (raudona, žalia)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalija (mėlyna, geltona)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Spalvų taisymas"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Koreguokite, kaip spalvos rodomos jūsų įrenginyje. Tai gali būti naudinga, kai norite:<br/><br/> <ol> <li> matyti tikslesnes spalvas;</li> <li> pašalinti spalvas, kad būtų lengviau susitelkti.</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Nepaisyta naudojant nuostatą „<xliff:g id="TITLE">%1$s</xliff:g>“"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Liko maždaug <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Kol išjungsite"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Ką tik"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefono garsiakalbis"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Šis telefonas"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Prisijungiant kilo problema. Išjunkite įrenginį ir vėl jį įjunkite"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Laidinis garso įrenginys"</string>
<string name="help_label" msgid="3528360748637781274">"Pagalba ir atsiliepimai"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 2633f13..5090df0 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomālija (sarkans/zaļš)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomālija (zils/dzeltens)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Krāsu korekcija"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Pielāgojiet krāsu attēlojumu jūsu ierīcē. Izmantojiet šo funkciju, lai:<br/><br/> <ol> <li> skatītu precīzāku krāsu attēlojumu;</li> <li> noņemtu krāsas, kad jāpievēršas kādam uzdevumam.</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Jaunā preference: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> — <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Aptuvenais atlikušais laiks: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -517,8 +516,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Līdz brīdim, kad izslēgsiet"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Tikko"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Tālruņa skaļrunis"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Šis tālrunis"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Radās problēma ar savienojuma izveidi. Izslēdziet un atkal ieslēdziet ierīci."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Vadu audioierīce"</string>
<string name="help_label" msgid="3528360748637781274">"Palīdzība un atsauksmes"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index e4173d0..c4890d4 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномалија (слепило за црвена и зелена)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомалија (слепило за сина и жолта)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Корекција на бои"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Приспособете го приказот на боите на уредот. Ова е корисно кога сакате:<br/><br/> <ol> <li> да гледате попрецизни бои</li> <li> да отстраните бои за подобра концентрација</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Прескокнато според <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Уште околу <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Додека не го исклучите"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Неодамнешни"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Телефонски звучник"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Овој телефон"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Проблем со поврзување. Исклучете го уредот и повторно вклучете го"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Жичен аудиоуред"</string>
<string name="help_label" msgid="3528360748637781274">"Помош и повратни информации"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 3cfe479..829d474 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"പ്രോട്ടാനോമലി (ചുവപ്പ്-പച്ച)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ട്രിട്ടാനോമലി (നീല-മഞ്ഞ)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"നിറം ക്രമീകരിക്കൽ"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"നിങ്ങളുടെ ഉപകരണത്തിൽ നിറങ്ങൾ എങ്ങനെ പ്രദർശിപ്പിക്കണമെന്ന് ക്രമീകരിക്കുക. ഇനിപ്പറയുന്ന കാര്യങ്ങൾ ചെയ്യാൻ ആഗ്രഹിക്കുമ്പോൾ ഇത് സഹായകരമാകും:<br/><br/> <ol> <li> നിറങ്ങൾ കൂടുതൽ കൃത്യമായി കാണാൻ</li> <li> ഫോക്കസ് ചെയ്യാൻ നിങ്ങളെ സഹായിക്കുന്നതിന് നിറങ്ങൾ നീക്കം ചെയ്യാൻ</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ഉപയോഗിച്ച് അസാധുവാക്കി"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"ഏതാണ്ട് <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"നിങ്ങൾ ഓഫാക്കുന്നത് വരെ"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ഇപ്പോൾ"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ഫോൺ സ്പീക്കർ"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"ഈ ഫോൺ"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"കണക്റ്റ് ചെയ്യുന്നതിൽ പ്രശ്നമുണ്ടായി. ഉപകരണം ഓഫാക്കി വീണ്ടും ഓണാക്കുക"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"വയർ മുഖേന ബന്ധിപ്പിച്ച ഓഡിയോ ഉപകരണം"</string>
<string name="help_label" msgid="3528360748637781274">"സഹായവും ഫീഡ്ബാക്കും"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index f8d9089..e9fe1dd 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"क्षीण रक्तवर्णांधता (लाल-हिरवा)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"रंग दृष्टी कमतरता (निळा-पिवळा)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"रंग सुधारणा"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"तुमच्या डिव्हाइसवर रंग कसे प्रदर्शित केले जातात ते अॅडजस्ट करा. तुम्हाला </br><br> </br><br> </br><br>अधिक स्पष्टपणे रंग पाहणे </br><br> </br><br> तुम्हाला फोकस करण्यात मदत करण्यासाठी रंग काढून टाकणे</br><br> </br><br> हे करायचे असते तेव्हा उपयुक्त असू शकते."</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारे अधिलिखित"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"अंदाजे <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाकी आहे"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"तुम्ही बंद करेपर्यंत"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"आत्ताच"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"फोनचा स्पीकर"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"हा फोन"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"कनेक्ट करण्यात समस्या आली. डिव्हाइस बंद करा आणि नंतर सुरू करा"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"वायर असलेले ऑडिओ डिव्हाइस"</string>
<string name="help_label" msgid="3528360748637781274">"मदत आणि फीडबॅक"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index f10e539..d80242ef 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomaly (အနီ-အစိမ်း)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaly (အပြာ-အဝါ)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"အရောင်ပြင်ဆင်မှု"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"သင့်စက်ပစ္စည်းတွင် အရောင်များပြသပုံကို ချိန်ညှိပါ။ ၎င်းက အောက်ပါတို့တွင် အသုံးဝင်နိုင်သည်-<br/><br/> <ol> <li> အရောင်များကို ပိုမိုတိကျစွာ မြင်လိုခြင်း</li> <li> သင်အာရုံစိုက်နိုင်ရန် အရောင်များကို ဖယ်ရှားခြင်း</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> မှ ကျော်၍ လုပ်ထားသည်။"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ခန့် ကျန်သည်"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"သင်ပိတ်လိုက်သည် အထိ"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ယခုလေးတင်"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ဖုန်းစပီကာ"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"ဤဖုန်း"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ချိတ်ဆက်ရာတွင် ပြဿနာရှိပါသည်။ စက်ကိုပိတ်ပြီး ပြန်ဖွင့်ပါ"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ကြိုးတပ် အသံစက်ပစ္စည်း"</string>
<string name="help_label" msgid="3528360748637781274">"အကူအညီနှင့် အကြံပြုချက်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 9fb68d0..7b49b40 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomali (rød-grønn)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomali (blå-gul)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Fargekorrigering"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Juster hvordan farger vises på enheten. Dette kan være nyttig når du vil<br/><br/> <ol> <li> se farger mer nøyaktig</li> <li> fjerne farger for å gjøre det enklere å fokusere</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Overstyres av <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Omtrent <xliff:g id="TIME_REMAINING">%1$s</xliff:g> gjenstår"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Til du slår av"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Nå nettopp"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefonhøyttaler"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Denne telefonen"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Tilkoblingsproblemer. Slå enheten av og på igjen"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Lydenhet med kabel"</string>
<string name="help_label" msgid="3528360748637781274">"Hjelp og tilbakemelding"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 4c847cf..4d380a5 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"प्रोटानेमली (रातो, हरियो)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ट्रिटानोमेली (निलो-पंहेलो)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"रङ्ग सुधार"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"तपाईंको यन्त्रमा रङहरू कस्ता देखिन्छन् भन्ने कुरा मिलाउनुहोस्। यो सुविधा निम्न अवस्थामा उपयोगी हुन सक्छ:<br/><br/> <ol> <li> तपाईं अझ सटीक रूपमा रङहरू देख्न चाहनुहुन्छ भने</li> <li> तपाईं कुनै कुरामा ध्यान केन्द्रित गर्न रङहरू हटाउन चाहनुहुन्छ भने</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारा अधिरोहित"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"लगभग <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाँकी छ"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"तपाईंले निष्क्रिय नपार्दासम्म"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"अहिले भर्खरै"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"फोनको स्पिकर"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"यो फोन"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"जोड्ने क्रममा समस्या भयो। यन्त्रलाई निष्क्रिय पारेर फेरि सक्रिय गर्नुहोस्"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"तारयुक्त अडियो यन्त्र"</string>
<string name="help_label" msgid="3528360748637781274">"मद्दत र प्रतिक्रिया"</string>
@@ -541,7 +539,7 @@
<string name="user_add_profile_item_title" msgid="3111051717414643029">"प्रतिबन्धित प्रोफाइल"</string>
<string name="user_add_user_title" msgid="5457079143694924885">"नयाँ प्रयोगकर्ता थप्ने हो?"</string>
<string name="user_add_user_message_long" msgid="1527434966294733380">"तपाईं थप प्रयोगकर्ताहरू सिर्जना गरेर ती प्रयोगकर्तालाई यो यन्त्र प्रयोग गर्न दिन सक्नुहुन्छ। हरेक प्रयोगकर्ताको आफ्नै ठाउँ हुन्छ। उनीहरू यो ठाउँमा आफ्नै एप, वालपेपर आदिका लागि प्रयोग गर्न सक्छन्। उनीहरू सबैजनालाई असर पार्ने Wi-Fi जस्ता यन्त्रका सेटिङहरू पनि परिवर्तन गर्न सक्छन्।\n\nतपाईंले नयाँ प्रयोगकर्ता थप्दा उक्त व्यक्तिले आफ्नो ठाउँ सेटअप गर्नु पर्ने हुन्छ।\n\nसबै प्रयोगकर्ता अन्य सबै प्रयोगकर्ताले प्रयोग गर्ने एपहरू अद्यावधिक गर्न सक्छन्। तर पहुँचसम्बन्धी सेटिङ तथा सेवाहरू नयाँ प्रयोगकर्तामा नसर्न सक्छ।"</string>
- <string name="user_add_user_message_short" msgid="3295959985795716166">"जब तपाईंले नयाँ प्रयोगकर्ता थप्नुहुन्छ, त्यो व्यक्तिले आफ्नो ठाउँ सेट गर्न आवश्यक छ।\n\nकुनै पनि प्रयोगकर्ताले सबै अन्य प्रयोगकर्ताहरूका लागि एपहरू अद्यावधिक गर्न सक्छन्।"</string>
+ <string name="user_add_user_message_short" msgid="3295959985795716166">"तपाईंले नयाँ प्रयोगकर्ता थप्नुभयो भने ती प्रयोगकर्ताले आफ्नो स्पेस सेट गर्नु पर्ने हुन्छ।\n\nसबै प्रयोगकर्ताले अरू प्रयोगकर्ताका एपहरू अपडेट गर्न सक्छन्।"</string>
<string name="user_setup_dialog_title" msgid="8037342066381939995">"अहिले प्रयोगकर्ता सेटअप गर्ने हो?"</string>
<string name="user_setup_dialog_message" msgid="269931619868102841">"यी व्यक्ति यन्त्र यो यन्त्र चलाउन र आफ्नो ठाउँ सेट गर्न उपलब्ध छन् भन्ने कुरा सुनिश्चित गर्नुहोस्"</string>
<string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"अहिले प्रोफाइल सेटअप गर्ने हो?"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 4cb755b..518aaa9 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -371,7 +371,7 @@
<string name="app_process_limit_title" msgid="8361367869453043007">"Achtergrondproceslimiet"</string>
<string name="show_all_anrs" msgid="9160563836616468726">"ANR\'s op de achtergrond"</string>
<string name="show_all_anrs_summary" msgid="8562788834431971392">"Dialoogvenster \'App reageert niet\' weergeven voor achtergrond-apps"</string>
- <string name="show_notification_channel_warnings" msgid="3448282400127597331">"Kanaalwaarschuwingen voor meldingen weergeven"</string>
+ <string name="show_notification_channel_warnings" msgid="3448282400127597331">"Kanaalwaarschuwingen voor meldingen tonen"</string>
<string name="show_notification_channel_warnings_summary" msgid="68031143745094339">"Geeft een waarschuwing op het scherm weer wanneer een app een melding post zonder geldig kanaal"</string>
<string name="force_allow_on_external" msgid="9187902444231637880">"Toestaan van apps op externe opslag afdwingen"</string>
<string name="force_allow_on_external_summary" msgid="8525425782530728238">"Hiermee komt elke app in aanmerking voor schrijven naar externe opslag, ongeacht de manifestwaarden"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index b7c93e5..b0fff21 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"ପ୍ରୋଟାନୋମାଲି (ଲାଲ୍-ସବୁଜ)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaly (ନୀଳ-ହଳଦିଆ)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ରଙ୍ଗ ସଂଶୋଧନ"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"ଆପଣଙ୍କ ଡିଭାଇସରେ ରଙ୍ଗଗୁଡ଼ିକ କିପରି ଡିସପ୍ଲେ ହୁଏ ତାହା ଆଡଜଷ୍ଟ କରନ୍ତୁ। ଆପଣ ଏହା କରିବାକୁ ଚାହିଁଲେ ଏହା ଉପଯୋଗୀ ହୋଇପାରେ:<br/><br/> <ol> <li> ଆହୁରି ସଠିକ୍ ଭାବେ ରଙ୍ଗଗୁଡ଼ିକୁ ଦେଖିବା</li> <li> ଆପଣଙ୍କୁ ଫୋକସ୍ କରିବାରେ ସାହାଯ୍ୟ କରିବାକୁ ରଙ୍ଗଗୁଡ଼ିକୁ କାଢ଼ିବା</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ଦ୍ୱାରା ଓଭର୍ରାଇଡ୍ କରାଯାଇଛି"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"ପାଖାପାଖି <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ବଳକା ଅଛି"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"ଆପଣ ବନ୍ଦ ନକରିବା ପର୍ଯ୍ୟନ୍ତ"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ଏହିକ୍ଷଣି"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ଫୋନ୍ ସ୍ପିକର୍"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"ଏହି ଫୋନ୍"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ସଂଯୋଗ କରିବାରେ ସମସ୍ୟା ହେଉଛି। ଡିଭାଇସ୍ ବନ୍ଦ କରି ପୁଣି ଚାଲୁ କରନ୍ତୁ"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ତାରଯୁକ୍ତ ଅଡିଓ ଡିଭାଇସ୍"</string>
<string name="help_label" msgid="3528360748637781274">"ସାହାଯ୍ୟ ଓ ମତାମତ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 896a100..71e2dba 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomaly (ਲਾਲ-ਹਰਾ)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaly (ਨੀਲਾ-ਪੀਲਾ)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ਰੰਗ ਸੁਧਾਈ"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"ਆਪਣੇ ਡੀਵਾਈਸ \'ਤੇ ਰੰਗਾਂ ਨੂੰ ਦਿਖਾਉਣ ਦੇ ਤਰੀਕੇ ਨੂੰ ਵਿਵਸਥਿਤ ਕਰੋ। ਇਹ ਉਦੋਂ ਲਾਹੇਵੰਦ ਹੋ ਸਕਦਾ ਹੈ ਜਦੋਂ ਤੁਸੀਂ ਇਹ ਕਰਨਾ ਚਾਹੋਗੇ:<br/><br/> <ol> <li> ਰੰਗਾਂ ਨੂੰ ਹੋਰ ਸਟੀਕਤਾ ਨਾਲ ਦੇਖਣਾ</li> <li> ਫੋਕਸ ਕਰਨ ਵਿੱਚ ਤੁਹਾਡੀ ਮਦਦ ਲਈ ਰੰਗਾਂ ਨੂੰ ਹਟਾਉਣਾ</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ਦੁਆਰਾ ਓਵਰਰਾਈਡ ਕੀਤਾ"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"ਲਗਭਗ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ਬਾਕੀ"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਬੰਦ ਨਹੀਂ ਕਰਦੇ"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ਹੁਣੇ ਹੀ"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ਫ਼ੋਨ ਦਾ ਸਪੀਕਰ"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"ਇਹ ਫ਼ੋਨ"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ਕਨੈਕਟ ਕਰਨ ਵਿੱਚ ਸਮੱਸਿਆ ਆਈ। ਡੀਵਾਈਸ ਨੂੰ ਬੰਦ ਕਰਕੇ ਵਾਪਸ ਚਾਲੂ ਕਰੋ"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ਤਾਰ ਵਾਲਾ ਆਡੀਓ ਡੀਵਾਈਸ"</string>
<string name="help_label" msgid="3528360748637781274">"ਮਦਦ ਅਤੇ ਵਿਚਾਰ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 663f3fc..c8d7287 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalia (czerwony-zielony)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalia (niebieski-żółty)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Korekcja kolorów"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Dostosuj sposób wyświetlania kolorów na ekranie urządzenia. Może to być pomocne, gdy chcesz:<br/><br/> <ol> <li> dokładniej widzieć kolory;,</li> <li> usunąć wybrane kolory, aby móc skuteczniej się skupić.</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Nadpisana przez <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Jeszcze około <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Dopóki nie wyłączysz"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Przed chwilą"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Głośnik telefonu"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ten telefon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem z połączeniem. Wyłącz i ponownie włącz urządzenie"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Przewodowe urządzenie audio"</string>
<string name="help_label" msgid="3528360748637781274">"Pomoc i opinie"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 8eab928..1d6624d 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalia (vermelho-verde)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalia (azul-amarelo)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correção de cor"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajuste as cores exibidas no seu dispositivo. Esta opção pode ser útil quando você quer:<br/><br/> <ol> <li> ver cores com mais precisão;</li> <li> remover cores para se concentrar melhor.</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Tempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Até você desativar"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Agora"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Alto-falante do smartphone"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este smartphone"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ocorreu um problema na conexão. Desligue o dispositivo e ligue-o novamente"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fio"</string>
<string name="help_label" msgid="3528360748637781274">"Ajuda e feedback"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index f28d70a..66d4b23 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalia (vermelho-verde)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalia (azul-amarelo)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correção da cor"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajuste a visualização das cores no dispositivo. Isto pode ser útil quando pretender:<br/><br/> <ol> <li> Ver cores com maior precisão</li> <li> Remover cores para melhorar a sua concentração</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Resta(m) cerca de <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Até desativar"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Agora mesmo"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altifalante do telemóvel"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este telemóvel"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problema ao ligar. Desligue e volte a ligar o dispositivo."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fios"</string>
<string name="help_label" msgid="3528360748637781274">"Ajuda e comentários"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 8eab928..1d6624d 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalia (vermelho-verde)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalia (azul-amarelo)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correção de cor"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajuste as cores exibidas no seu dispositivo. Esta opção pode ser útil quando você quer:<br/><br/> <ol> <li> ver cores com mais precisão;</li> <li> remover cores para se concentrar melhor.</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Tempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Até você desativar"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Agora"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Alto-falante do smartphone"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este smartphone"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ocorreu um problema na conexão. Desligue o dispositivo e ligue-o novamente"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fio"</string>
<string name="help_label" msgid="3528360748637781274">"Ajuda e feedback"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 0a4e462..3e77ab6 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (roșu-verde)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (albastru-galben)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Corecția culorii"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajustați modul în care se afișează culorile pe dispozitiv. Acest lucru poate fi util când doriți să:<br/><br/> <ol> <li> vedeți culorile mai bine</li> <li> eliminați culorile pentru a vă ajuta să vă concentrați</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Valoare înlocuită de <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Timp aproximativ rămas: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -517,8 +516,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Până când dezactivați"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Chiar acum"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Difuzorul telefonului"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Acest telefon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problemă la conectare. Opriți și reporniți dispozitivul."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispozitiv audio cu fir"</string>
<string name="help_label" msgid="3528360748637781274">"Ajutor și feedback"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index b030715..b342287 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномалия (красный/зеленый)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомалия (синий/желтый)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Коррекция цвета"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Настройте цветопередачу на экране устройства. Эта функция может помочь:<br/><br/> <ol> <li> сделать цвета более четкими;</li> <li> убрать цвета, чтобы вам проще было сфокусироваться.</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Новая настройка: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"Уровень заряда – <xliff:g id="PERCENTAGE">%1$s</xliff:g>. <xliff:g id="TIME_STRING">%2$s</xliff:g>."</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Заряда хватит примерно на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Пока вы не отключите"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Только что"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Встроенный динамик"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Этот смартфон"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ошибка подключения. Выключите и снова включите устройство."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Проводное аудиоустройство"</string>
<string name="help_label" msgid="3528360748637781274">"Справка/отзыв"</string>
@@ -559,7 +557,7 @@
<string name="user_switch_to_user" msgid="6975428297154968543">"Переключиться на этот аккаунт: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Создаем нового пользователя…"</string>
<string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string>
- <string name="guest_new_guest" msgid="3482026122932643557">"Добавить аккаунт гостя"</string>
+ <string name="guest_new_guest" msgid="3482026122932643557">"Добавить гостя"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Удалить аккаунт гостя"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Гость"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Сделать снимок"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index d9d5351..b24ac8e 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomália (červená a zelená)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomália (modrá a žltá)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Úprava farieb"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Upravte si zobrazovanie farieb v zariadení. Môže to byť užitočné, ak chcete:<br/><br/> <ol> <li> zobraziť presnejšie viac farieb;y</li> <li> odstrániť farby, aby ste sa mohli lepšie sústrediť.</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Prekonané predvoľbou <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Zostáva približne <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Dokým funkciu nevypnete"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Teraz"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Reproduktor telefónu"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tento telefón"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Pri pripájaní sa vyskytol problém. Zariadenie vypnite a znova zapnite."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Audio zariadenie s káblom"</string>
<string name="help_label" msgid="3528360748637781274">"Pomocník a spätná väzba"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 81b1b60..626402a 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalija (rdeča – zelena)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalija (modra – rumena)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Popravljanje barv"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Prilagodite prikaz barv v napravi. To je uporabno, ko želite:<br/><br/> <ol> <li> videti bolj prave barve;</li> <li> odstraniti barve, da se lažje osredotočite.</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Preglasila nastavitev: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Še približno <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Dokler ne izklopite"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Pravkar"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Zvočnik telefona"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ta telefon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Težava pri povezovanju. Napravo izklopite in znova vklopite."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žična zvočna naprava"</string>
<string name="help_label" msgid="3528360748637781274">"Pomoč in povratne informacije"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index b71e51f..4f22348 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomali (e kuqe - e gjelbër)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomali (e kaltër - e verdhë)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Korrigjimi i ngjyrës"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Rregullo mënyrën se si ngjyrat afishohen në pajisjen tënde. Kjo mund të jetë e dobishme kur dëshiron që:<br/><br/> <ol> <li> T\'i shikosh ngjyrat me më shumë saktësi</li> <li> T\'i heqësh ngjyrat për të të ndihmuar të fokusohesh</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Mbivendosur nga <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Rreth <xliff:g id="TIME_REMAINING">%1$s</xliff:g> të mbetura"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Deri sa ta çaktivizosh"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Pikërisht tani"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altoparlanti i telefonit"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ky telefon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem me lidhjen. Fike dhe ndize përsëri pajisjen"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Pajisja audio me tel"</string>
<string name="help_label" msgid="3528360748637781274">"Ndihma dhe komentet"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 2ec2ded..069b6a5 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomali (rött-grönt)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomali (blått-gult)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Färgkorrigering"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ändra hur färger visas på enheten. Det kan vara ett bra hjälpmedel när du vill<br/><br/> <ol> <li> att färger ska visas mer exakt</li> <li> ta bort färger för att fokusera bättre</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Har åsidosatts av <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Cirka <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kvar"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Tills du inaktiverar funktionen"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Nyss"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefonens högtalare"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Den här telefonen"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Det gick inte att ansluta. Stäng av enheten och slå på den igen"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Ljudenhet med kabelanslutning"</string>
<string name="help_label" msgid="3528360748637781274">"Hjälp och feedback"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 410a4aa..0fb620c 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -555,7 +555,7 @@
<string name="user_switch_to_user" msgid="6975428297154968543">"Badili utumie <xliff:g id="USER_NAME">%s</xliff:g>"</string>
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Inaweka mtumiaji mpya…"</string>
<string name="user_nickname" msgid="262624187455825083">"Jina wakilishi"</string>
- <string name="guest_new_guest" msgid="3482026122932643557">"Weka mgeni"</string>
+ <string name="guest_new_guest" msgid="3482026122932643557">"Ongeza mgeni"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ondoa mgeni"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Mgeni"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Piga picha"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 14eb25d..3106a69 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"நிறம் அடையாளங்காண முடியாமை (சிவப்பு-பச்சை)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"நிறம் அடையாளங்காண முடியாமை (நீலம்-மஞ்சள்)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"வண்ணத்திருத்தம்"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"சாதனத்தில் வண்ணங்கள் காண்பிக்கப்படும் விதத்தைச் சரிசெய்யலாம். இதன் மூலம் நீங்கள் விரும்பும்போதெல்லாம்:<br/><br/> <ol> <li> வண்ணங்களை மிகத் தெளிவாகப் பார்க்கலாம்</li> <li> கவனம் சிதறாமல் இருக்க வண்ணங்களை நீக்கலாம்</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> மூலம் மேலெழுதப்பட்டது"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"கிட்டத்தட்ட <xliff:g id="TIME_REMAINING">%1$s</xliff:g> மீதமுள்ளது"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"ஆஃப் செய்யும் வரை"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"சற்றுமுன்"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"மொபைல் ஸ்பீக்கர்"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"இந்த மொபைல்"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"இணைப்பதில் சிக்கல். சாதனத்தை ஆஃப் செய்து மீண்டும் ஆன் செய்யவும்"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"வயருடன்கூடிய ஆடியோ சாதனம்"</string>
<string name="help_label" msgid="3528360748637781274">"உதவியும் கருத்தும்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index e652ab7..1f78c0c 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"ప్రొటానోమలీ (ఎరుపు-ఆకుపచ్చ రంగు)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ట్రైటనోమలీ (నీలం-పసుపు రంగు)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"కలర్ సరిచేయడం"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"మీ పరికరంపై రంగులు కనిపించే విధానాన్ని అడ్జస్ట్ చేయండి. మీకు కావలసినప్పుడు, ఇది సహాయకరంగా ఉంటుంది:<br/><br/> <ol> <li> మరింత ఖచ్చితంగా రంగులను చూడండి</li> <li> మీరు ఫోకస్ చేయడంలో సహాయపడటానికి రంగులను తీసివేయండి</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ద్వారా భర్తీ చేయబడింది"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> సమయం మిగిలి ఉంది"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"మీరు ఆఫ్ చేసే వరకు"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ఇప్పుడే"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ఫోన్ స్పీకర్"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"ఈ ఫోన్"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"కనెక్ట్ చేయడంలో సమస్య ఉంది. పరికరాన్ని ఆఫ్ చేసి, ఆపై తిరిగి ఆన్ చేయండి"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"వైర్ గల ఆడియో పరికరం"</string>
<string name="help_label" msgid="3528360748637781274">"సహాయం & ఫీడ్బ్యాక్"</string>
@@ -557,7 +555,7 @@
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>కు మార్చు"</string>
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"కొత్త యూజర్ను క్రియేట్ చేస్తోంది…"</string>
<string name="user_nickname" msgid="262624187455825083">"మారుపేరు"</string>
- <string name="guest_new_guest" msgid="3482026122932643557">"అతిథిని జోడించండి"</string>
+ <string name="guest_new_guest" msgid="3482026122932643557">"గెస్ట్ను జోడించండి"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"అతిథిని తీసివేయండి"</string>
<string name="guest_nickname" msgid="6332276931583337261">"అతిథి"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"ఒక ఫోటో తీయండి"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 5fbf5d4..ef516c4 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomali (kırmızı-yeşil)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomali (mavi-sarı)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Renk düzeltme"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Renklerin cihazınızda nasıl görüntüleneceğini düzenleyin Bu, şunları yapmak istediğinizde kullanışlı olur:<br/><br/> <ol> <li> Renkleri daha doğru görmek</li> <li> Odaklanmanıza yardımcı olması için renkleri kaldırmak</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> tarafından geçersiz kılındı"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Yaklaşık <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kaldı"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Siz kapatana kadar"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Az önce"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefon hoparlörü"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Bu telefon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Bağlanırken sorun oluştu. Cihazı kapatıp tekrar açın"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kablolu ses cihazı"</string>
<string name="help_label" msgid="3528360748637781274">"Yardım ve geri bildirim"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 4511a4b..81528ad 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномалія (червоний – зелений)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомалія (синій – жовтий)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Корекція кольору"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Налаштуйте відтворення кольорів на екрані пристрою. Це може бути корисно, якщо ви хочете:<br/><br/> <ol> <li> точніше відтворювати кольори;</li> <li> вилучити кольори, щоб зосередитися на головному.</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Замінено на <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Залишилося приблизно <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Доки не вимкнути"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Щойно"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Динамік телефона"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Цей телефон"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Не вдається підключитися. Перезавантажте пристрій."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Дротовий аудіопристрій"</string>
<string name="help_label" msgid="3528360748637781274">"Довідка й відгуки"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index e61d281..798c885 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomaly (سرخ سبز)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaly (نیلا پیلا)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"رنگ کی اصلاح"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"آپ کے آلے پر رنگوں کے ڈسپلے ہونے کے طریقے کو ایڈجسٹ کریں۔ یہ خصوصیت درج ذیل کے لیے مددگار ثابت ہو سکتی ہے:<br/><br/> <ol> <li> جب آپ رنگوں کو مزید درست طریقے سے دیکھنا چاہیں </li> <li> فوکس کرنے میں مدد کے لیے رنگوں کو ہٹادیں</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> کے ذریعہ منسوخ کردیا گیا"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"تقریباً <xliff:g id="TIME_REMAINING">%1$s</xliff:g> باقی ہے"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"یہاں تک کہ آپ آف کر دیں"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ابھی ابھی"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"فون اسپیکر"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"یہ فون"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"منسلک کرنے میں مسئلہ پیش آ گیا۔ آلہ کو آف اور بیک آن کریں"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"وائرڈ آڈیو آلہ"</string>
<string name="help_label" msgid="3528360748637781274">"مدد اور تاثرات"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 0730c6c..6efc0be 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -556,7 +556,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Yangi foydalanuvchi yaratilmoqda…"</string>
<string name="user_nickname" msgid="262624187455825083">"Nik"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Mehmon kiritish"</string>
- <string name="guest_exit_guest" msgid="5908239569510734136">"Mehmon rejimini olib tashlash"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Mehmonni olib tashlash"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Mehmon"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Suratga olish"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Rasm tanlash"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 7d1bde3..67923c5 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"红色弱视(红绿不分)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"蓝色弱视(蓝黄不分)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"色彩校正"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"调整设备上的颜色显示方式。此设置适用于以下情况:<br/><br/> <ol> <li>想要更准确地查看颜色</li> <li>想要去除颜色,以便集中注意力</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"已被“<xliff:g id="TITLE">%1$s</xliff:g>”覆盖"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"大约还可使用 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"直到您将其关闭"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"刚刚"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"手机扬声器"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"这部手机"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"连接时遇到问题。请关闭并重新开启设备"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有线音频设备"</string>
<string name="help_label" msgid="3528360748637781274">"帮助和反馈"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 06b5cd0..bf69b31 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -424,7 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"紅色弱視 (紅綠)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"藍色弱視 (藍黃)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"色彩校正"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"調整裝置顯示顏色的方式。這項設定適用於以下情況:<br/><br/> <ol> <li> 想讓裝置更準確地顯示顏色</li> <li> 移除顏色以提高專注力</li> </ol>"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"調整裝置顯示顏色嘅方式。呢項設定喺以下情況適用:<br/><br/> <ol> <li> 想令裝置更加準確咁顯示顏色</li> <li> 移除顏色嚟提高專注力</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"已由「<xliff:g id="TITLE">%1$s</xliff:g>」覆寫"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"還有大約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -515,7 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"直至您關閉為止"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"剛剛"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"手機喇叭"</string>
- <string name="media_transfer_this_phone" msgid="7194341457812151531">"這支手機"</string>
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"這部手機"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"無法連接,請關閉裝置然後重新開機"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有線音響裝置"</string>
<string name="help_label" msgid="3528360748637781274">"說明與意見反映"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 2297cbe..7b0274a 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"I-Protanomaly (bomvu-luhlaza)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"I-Tritanomaly (luhlaza okwesibhakabhaka-phuzi)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Ukulungiswa kombala"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Lungisa indlela imibala eboniswa ngayo kudivayisi yakkho. Lokhu kungaba usizo lapho ufuna:<br/><br/> <ol> <li> Ukubona imibala ngokunembilie</li> <li> Ukususa imibala ukuze ugxile</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Igitshezwe ngaphezulu yi-<xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Cishe u-<xliff:g id="TIME_REMAINING">%1$s</xliff:g> osele"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Uze uvale isikrini"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Khona manje"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Isipikha sefoni"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Le foni"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Inkinga yokuxhumeka. Vala idivayisi futhi uphinde uyivule"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Idivayisi yomsindo enentambo"</string>
<string name="help_label" msgid="3528360748637781274">"Usizo nempendulo"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 8fd1910..129aca4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -386,7 +386,6 @@
case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT:
case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS:
case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED:
- case BluetoothDevice.UNBOND_REASON_REMOVED:
errorMsg = R.string.bluetooth_pairing_error_message;
break;
default:
diff --git a/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS b/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS
index e7a20b3..c88ed8e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS
@@ -1,6 +1,7 @@
# Default reviewers for this and subdirectories.
andychou@google.com
arcwang@google.com
+changbetty@google.com
goldmanj@google.com
qal@google.com
wengsu@google.com
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS b/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
index 0bde5c0..f3b600c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
@@ -2,6 +2,7 @@
andychou@google.com
arcwang@google.com
asapperstein@google.com
+changbetty@google.com
goldmanj@google.com
qal@google.com
wengsu@google.com
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index 6a4d650..4bff78f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -353,21 +353,6 @@
}
@Test
- public void showUnbondMessage_reasonRemoved_showCorrectedErrorCode() {
- mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
- mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
- mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
- mIntent.putExtra(BluetoothDevice.EXTRA_REASON, BluetoothDevice.UNBOND_REASON_REMOVED);
- when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1);
- when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME);
-
- mContext.sendBroadcast(mIntent);
-
- verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME),
- eq(R.string.bluetooth_pairing_error_message));
- }
-
- @Test
public void showUnbondMessage_reasonAuthTimeout_showCorrectedErrorCode() {
mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index e8e10a4..db9b83e 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -281,5 +281,6 @@
VALIDATORS.put(Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY,
new InclusiveFloatRangeValidator(0.0f, 1.0f));
VALIDATORS.put(Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index a0b9528..7288371 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1990,6 +1990,13 @@
dumpSetting(s, p,
Settings.Secure.CARRIER_APPS_HANDLED,
SecureSettingsProto.CARRIER_APPS_HANDLED);
+
+ final long clipboardToken = p.start(SecureSettingsProto.CLIPBOARD);
+ dumpSetting(s, p,
+ Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS,
+ SecureSettingsProto.Clipboard.SHOW_ACCESS_NOTIFICATIONS);
+ p.end(clipboardToken);
+
dumpSetting(s, p,
Settings.Secure.CMAS_ADDITIONAL_BROADCAST_PKG,
SecureSettingsProto.CMAS_ADDITIONAL_BROADCAST_PKG);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index b4194fd..cf66bad 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -187,6 +187,7 @@
<uses-permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS" />
<uses-permission android:name="android.permission.MANAGE_SENSORS" />
<uses-permission android:name="android.permission.MANAGE_AUDIO_POLICY" />
+ <uses-permission android:name="android.permission.QUERY_AUDIO_STATE" />
<uses-permission android:name="android.permission.MANAGE_CAMERA" />
<!-- Permissions needed to test system only camera devices -->
<uses-permission android:name="android.permission.CAMERA" />
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index b6fd286..6574353 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -54,7 +54,7 @@
name: "SystemUI-sensors",
srcs: [
"src/com/android/systemui/util/sensors/ThresholdSensor.java",
- ]
+ ],
}
android_library {
@@ -99,7 +99,7 @@
"SystemUI-tags",
"SystemUI-proto",
"dagger2",
- "jsr330"
+ "jsr330",
],
manifest: "AndroidManifest.xml",
diff --git a/packages/SystemUI/plugin/Android.bp b/packages/SystemUI/plugin/Android.bp
index 9e67e4b..d6204db 100644
--- a/packages/SystemUI/plugin/Android.bp
+++ b/packages/SystemUI/plugin/Android.bp
@@ -25,7 +25,10 @@
name: "SystemUIPluginLib",
- srcs: ["src/**/*.java"],
+ srcs: [
+ "src/**/*.java",
+ "bcsmartspace/src/**/*.java",
+ ],
static_libs: [
"PluginCoreLib",
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
new file mode 100644
index 0000000..f8a9a045
--- /dev/null
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.plugins;
+
+import android.os.Parcelable;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+import java.util.List;
+
+/**
+ * Interface to provide SmartspaceTargets to BcSmartspace.
+ */
+@ProvidesInterface(action = BcSmartspaceDataPlugin.ACTION, version = BcSmartspaceDataPlugin.VERSION)
+public interface BcSmartspaceDataPlugin extends Plugin {
+ String ACTION = "com.android.systemui.action.PLUGIN_BC_SMARTSPACE_DATA";
+ int VERSION = 1;
+
+ /** Register a listener to get Smartspace data. */
+ void registerListener(SmartspaceTargetListener listener);
+
+ /** Unregister a listener. */
+ void unregisterListener(SmartspaceTargetListener listener);
+
+ /** Provides Smartspace data to registered listeners. */
+ interface SmartspaceTargetListener {
+ /** Each Parcelable is a SmartspaceTarget that represents a card. */
+ void onSmartspaceTargetsUpdated(List<? extends Parcelable> targets);
+ }
+}
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 92d9ca9..b0c5239 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -588,4 +588,7 @@
<!-- Determines whether the shell features all run on another thread. -->
<bool name="config_enableShellMainThread">false</bool>
+
+ <!-- Determines whether to allow the nav bar handle to be forced to be opaque. -->
+ <bool name="allow_force_nav_bar_handle_opaque">true</bool>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6207449..db79e96 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -851,8 +851,6 @@
<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] -->
@@ -2297,6 +2295,9 @@
<!-- accessibility label for button to select user [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_user">Signed in as <xliff:g name="user" example="John">%s</xliff:g></string>
+ <!-- Accessibility description (not shown on the screen) of action to open user switcher when the multi-user icon is clicked. It will read as "Double-tap to choose user" in screen readers [CHAR LIMIT=NONE] -->
+ <string name="accessibility_quick_settings_choose_user_action">choose user</string>
+
<!-- Content description for no internet connection [CHAR LIMIT=NONE] -->
<string name="data_connection_no_internet">No internet</string>
@@ -2651,7 +2652,7 @@
<!-- Content description for magnification mode switch. [CHAR LIMIT=NONE] -->
<string name="magnification_mode_switch_description">Magnification switch</string>
<!-- A11y state description for magnification mode switch that device is in full-screen mode. [CHAR LIMIT=NONE] -->
- <string name="magnification_mode_switch_state_full_screen">Magnify entire screen</string>
+ <string name="magnification_mode_switch_state_full_screen">Magnify full screen</string>
<!-- A11y state description for magnification mode switch that device is in window mode. [CHAR LIMIT=NONE] -->
<string name="magnification_mode_switch_state_window">Magnify part of screen</string>
<!-- Click action label for magnification switch. [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
index 72e4061..f50c3c92 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
@@ -20,7 +20,6 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.view.Choreographer;
-import android.view.Surface;
import android.view.SurfaceControl;
/**
@@ -91,24 +90,6 @@
.setPosition(leash, positionX, positionY);
}
- public void reset(SurfaceControl.Transaction tx, SurfaceControl leash, Rect destinationBounds,
- @Surface.Rotation int rotation) {
- resetScale(tx, leash, destinationBounds);
- if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
- final int degree = (rotation == Surface.ROTATION_90) ? -90 : 90;
- mTmpTransform.setRotate(degree, 0, 0);
- tx.setMatrix(leash, mTmpTransform, mTmpFloat9);
- }
- resetCornerRadius(tx, leash);
- crop(tx, leash, destinationBounds);
- }
-
- public void resetScale(SurfaceControl.Transaction tx, SurfaceControl leash,
- Rect destinationBounds) {
- tx.setMatrix(leash, Matrix.IDENTITY_MATRIX, mTmpFloat9)
- .setPosition(leash, destinationBounds.left, destinationBounds.top);
- }
-
public void resetCornerRadius(SurfaceControl.Transaction tx, SurfaceControl leash) {
tx.setCornerRadius(leash, 0);
}
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 49e86f5..5126284 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
@@ -63,15 +63,9 @@
Rect getNonMinimizedSplitScreenSecondaryBounds() = 7;
/**
- * Control the {@param alpha} of the back button in the navigation bar and {@param animate} if
- * needed from current value
- * @deprecated
- */
- void setBackButtonAlpha(float alpha, boolean animate) = 8;
-
- /**
* Control the {@param alpha} of the option nav bar button (back-button in 2 button mode
- * and home bar in no-button mode) and {@param animate} if needed from current value
+ * and home handle & background in gestural mode). The {@param animate} is currently only
+ * supported for 2 button mode.
*/
void setNavBarButtonAlpha(float alpha, boolean animate) = 19;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index c2d52a7..bf0d29a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -76,10 +76,14 @@
* accordingly. This should be called before `finish`
* @param taskId Task id of the Activity in PiP mode.
* @param destinationBounds Bounds of the PiP window on home.
+ * @param windowCrop bounds to crop as part of final transform.
+ * @param float9 An array of 9 floats to be used as matrix transform.
*/
- public void setFinishTaskBounds(int taskId, Rect destinationBounds) {
+ public void setFinishTaskBounds(int taskId, Rect destinationBounds, Rect windowCrop,
+ float[] float9) {
try {
- mAnimationController.setFinishTaskBounds(taskId, destinationBounds);
+ mAnimationController.setFinishTaskBounds(taskId, destinationBounds, windowCrop,
+ float9);
} catch (RemoteException e) {
Log.d(TAG, "Failed to set finish task bounds", e);
}
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
index 7ba9069..06155bc 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -137,8 +137,11 @@
mWrapped.hideCurrentInputMethod();
}
- @Override public void setFinishTaskBounds(int taskId, Rect destinationBounds) {
- if (mWrapped != null) mWrapped.setFinishTaskBounds(taskId, destinationBounds);
+ @Override public void setFinishTaskBounds(int taskId, Rect destinationBounds,
+ Rect windowCrop, float[] float9) {
+ if (mWrapped != null) {
+ mWrapped.setFinishTaskBounds(taskId, destinationBounds, windowCrop, float9);
+ }
}
@Override public void finish(boolean toHome, boolean sendUserLeaveHint) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index a580663..9f32c03 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -185,8 +185,8 @@
if (timeoutMs == 0) {
mMessageAreaController.setMessage(mView.getWrongPasswordStringId());
}
+ mView.resetPasswordText(true /* animate */, false /* announce deletion if no match */);
}
- mView.resetPasswordText(true /* animate */, !matched /* announce deletion if no match */);
}
protected void verifyPasswordAndUnlock() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
index de64f07..40190c1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
@@ -31,6 +31,7 @@
* A Base class for all Keyguard password/pattern/pin related inputs.
*/
public abstract class KeyguardInputView extends LinearLayout {
+ private Runnable mOnFinishImeAnimationRunnable;
public KeyguardInputView(Context context) {
super(context);
@@ -47,7 +48,8 @@
abstract CharSequence getTitle();
- void animateForIme(float interpolatedFraction) {
+ void animateForIme(float interpolatedFraction, boolean appearingAnim) {
+ return;
}
boolean disallowInterceptTouch(MotionEvent event) {
@@ -85,4 +87,14 @@
};
}
+ public void setOnFinishImeAnimationRunnable(Runnable onFinishImeAnimationRunnable) {
+ mOnFinishImeAnimationRunnable = onFinishImeAnimationRunnable;
+ }
+
+ public void runOnFinishImeAnimationRunnable() {
+ if (mOnFinishImeAnimationRunnable != null) {
+ mOnFinishImeAnimationRunnable.run();
+ mOnFinishImeAnimationRunnable = null;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 533bec1..cd9627f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -16,6 +16,8 @@
package com.android.keyguard;
+import static android.view.WindowInsets.Type.ime;
+
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
@@ -23,15 +25,25 @@
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.content.Context;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.view.WindowInsetsAnimationControlListener;
+import android.view.WindowInsetsAnimationController;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.TextViewInputDisabler;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
/**
* Displays an alphanumeric (latin-1) key entry for the user to enter
@@ -41,6 +53,8 @@
private final int mDisappearYTranslation;
+ private static final long IME_DISAPPEAR_DURATION_MS = 125;
+
// A delay constant to be used in a workaround for the situation where InputMethodManagerService
// is not switched to the new user yet.
// TODO: Remove this by ensuring such a race condition never happens.
@@ -150,9 +164,63 @@
}
@Override
- public void animateForIme(float interpolatedFraction) {
+ public boolean startDisappearAnimation(Runnable finishRunnable) {
+ getWindowInsetsController().controlWindowInsetsAnimation(ime(),
+ 100,
+ Interpolators.LINEAR, null, new WindowInsetsAnimationControlListener() {
+
+ @Override
+ public void onReady(@NonNull WindowInsetsAnimationController controller,
+ int types) {
+ ValueAnimator anim = ValueAnimator.ofFloat(1f, 0f);
+ anim.addUpdateListener(animation -> {
+ if (controller.isCancelled()) {
+ return;
+ }
+ Insets shownInsets = controller.getShownStateInsets();
+ Insets insets = Insets.add(shownInsets, Insets.of(0, 0, 0,
+ (int) (-shownInsets.bottom / 4
+ * anim.getAnimatedFraction())));
+ controller.setInsetsAndAlpha(insets,
+ (float) animation.getAnimatedValue(),
+ anim.getAnimatedFraction());
+ });
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ controller.finish(false);
+ runOnFinishImeAnimationRunnable();
+ finishRunnable.run();
+ }
+ });
+ anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+ anim.start();
+ }
+
+ @Override
+ public void onFinished(
+ @NonNull WindowInsetsAnimationController controller) {
+ }
+
+ @Override
+ public void onCancelled(
+ @Nullable WindowInsetsAnimationController controller) {
+ }
+ });
+ return true;
+ }
+
+
+ @Override
+ public void animateForIme(float interpolatedFraction, boolean appearingAnim) {
animate().cancel();
- setAlpha(Math.max(interpolatedFraction, getAlpha()));
+ setAlpha(appearingAnim
+ ? Math.max(interpolatedFraction, getAlpha())
+ : 1 - interpolatedFraction);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index 57b8cf0..e45dd8b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -189,23 +189,22 @@
return;
}
if (wasDisabled) {
- mInputMethodManager.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
+ showInput();
}
}
@Override
public void onResume(int reason) {
super.onResume(reason);
-
- mPasswordEntry.requestFocus();
if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) {
showInput();
}
}
private void showInput() {
- mPasswordEntry.post(() -> {
- if (mPasswordEntry.isFocused() && mView.isShown()) {
+ mView.post(() -> {
+ if (mView.isShown()) {
+ mPasswordEntry.requestFocus();
mInputMethodManager.showSoftInput(
mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
}
@@ -214,7 +213,18 @@
@Override
public void onPause() {
- super.onPause();
+ if (!mPasswordEntry.isVisibleToUser()) {
+ // Reset all states directly and then hide IME when the screen turned off.
+ super.onPause();
+ } else {
+ // In order not to break the IME hide animation by resetting states too early after
+ // the password checked, make sure resetting states after the IME hiding animation
+ // finished.
+ mView.setOnFinishImeAnimationRunnable(() -> {
+ mPasswordEntry.clearFocus();
+ super.onPause();
+ });
+ }
mInputMethodManager.hideSoftInputFromWindow(mView.getWindowToken(), 0);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index eaf8516..4887767 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -23,11 +23,9 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
-import android.graphics.Insets;
import android.graphics.Rect;
import android.provider.Settings;
import android.util.AttributeSet;
@@ -42,12 +40,9 @@
import android.view.ViewPropertyAnimator;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
-import android.view.WindowInsetsAnimationControlListener;
-import android.view.WindowInsetsAnimationController;
import android.view.WindowManager;
import android.widget.FrameLayout;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.dynamicanimation.animation.DynamicAnimation;
@@ -130,6 +125,9 @@
WindowInsetsAnimation.Bounds bounds) {
if (!mDisappearAnimRunning) {
beginJankInstrument(InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_APPEAR);
+ } else {
+ beginJankInstrument(
+ InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
}
mSecurityViewFlipper.getBoundsOnScreen(mFinalBounds);
return bounds;
@@ -138,25 +136,28 @@
@Override
public WindowInsets onProgress(WindowInsets windowInsets,
List<WindowInsetsAnimation> list) {
- if (mDisappearAnimRunning) {
- mSecurityViewFlipper.setTranslationY(
- mInitialBounds.bottom - mFinalBounds.bottom);
- } else {
- int translationY = 0;
- float interpolatedFraction = 1f;
- for (WindowInsetsAnimation animation : list) {
- if ((animation.getTypeMask() & WindowInsets.Type.ime()) == 0) {
- continue;
- }
- interpolatedFraction = animation.getInterpolatedFraction();
-
- final int paddingBottom = (int) MathUtils.lerp(
- mInitialBounds.bottom - mFinalBounds.bottom, 0,
- interpolatedFraction);
- translationY += paddingBottom;
+ float start = mDisappearAnimRunning
+ ? -(mFinalBounds.bottom - mInitialBounds.bottom)
+ : mInitialBounds.bottom - mFinalBounds.bottom;
+ float end = mDisappearAnimRunning
+ ? -((mFinalBounds.bottom - mInitialBounds.bottom) * 0.75f)
+ : 0f;
+ int translationY = 0;
+ float interpolatedFraction = 1f;
+ for (WindowInsetsAnimation animation : list) {
+ if ((animation.getTypeMask() & WindowInsets.Type.ime()) == 0) {
+ continue;
}
- mSecurityViewFlipper.animateForIme(translationY, interpolatedFraction);
+ interpolatedFraction = animation.getInterpolatedFraction();
+
+ final int paddingBottom = (int) MathUtils.lerp(
+ start, end,
+ interpolatedFraction);
+ translationY += paddingBottom;
}
+ mSecurityViewFlipper.animateForIme(translationY, interpolatedFraction,
+ !mDisappearAnimRunning);
+
return windowInsets;
}
@@ -164,7 +165,10 @@
public void onEnd(WindowInsetsAnimation animation) {
if (!mDisappearAnimRunning) {
endJankInstrument(InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_APPEAR);
- mSecurityViewFlipper.animateForIme(0, /* interpolatedFraction */ 1f);
+ mSecurityViewFlipper.animateForIme(0, /* interpolatedFraction */ 1f,
+ true /* appearingAnim */);
+ } else {
+ endJankInstrument(InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
}
}
};
@@ -522,63 +526,6 @@
public void startDisappearAnimation(SecurityMode securitySelection) {
mDisappearAnimRunning = true;
- if (securitySelection == SecurityMode.Password) {
- mSecurityViewFlipper.getWindowInsetsController().controlWindowInsetsAnimation(ime(),
- IME_DISAPPEAR_DURATION_MS,
- Interpolators.LINEAR, null, new WindowInsetsAnimationControlListener() {
-
-
- @Override
- public void onReady(@NonNull WindowInsetsAnimationController controller,
- int types) {
- ValueAnimator anim = ValueAnimator.ofFloat(1f, 0f);
- anim.addUpdateListener(animation -> {
- if (controller.isCancelled()) {
- return;
- }
- Insets shownInsets = controller.getShownStateInsets();
- Insets insets = Insets.add(shownInsets, Insets.of(0, 0, 0,
- (int) (-shownInsets.bottom / 4
- * anim.getAnimatedFraction())));
- controller.setInsetsAndAlpha(insets,
- (float) animation.getAnimatedValue(),
- anim.getAnimatedFraction());
- });
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- beginJankInstrument(
- InteractionJankMonitor
- .CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- endJankInstrument(
- InteractionJankMonitor
- .CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
- controller.finish(false);
- }
- });
- anim.setDuration(IME_DISAPPEAR_DURATION_MS);
- anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
- anim.start();
- }
-
- @Override
- public void onFinished(
- @NonNull WindowInsetsAnimationController controller) {
- mDisappearAnimRunning = false;
- }
-
- @Override
- public void onCancelled(
- @Nullable WindowInsetsAnimationController controller) {
- cancelJankInstrument(
- InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
- }
- });
- }
}
private void beginJankInstrument(int cuj) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index 75ef4b32..e01e17d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -87,10 +87,10 @@
* Translate the entire view, and optionally inform the wrapped view of the progress
* so it can animate with the parent.
*/
- public void animateForIme(int translationY, float interpolatedFraction) {
+ public void animateForIme(int translationY, float interpolatedFraction, boolean appearingAnim) {
super.setTranslationY(translationY);
KeyguardInputView v = getSecurityView();
- if (v != null) v.animateForIme(interpolatedFraction);
+ if (v != null) v.animateForIme(interpolatedFraction, appearingAnim);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
index 97d6e97..14b9691 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
@@ -15,6 +15,7 @@
*/
package com.android.keyguard;
+import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -34,7 +35,9 @@
* Provides background color and radius animations for key pad buttons.
*/
class NumPadAnimator {
- private ValueAnimator mAnimator;
+ private AnimatorSet mAnimator;
+ private ValueAnimator mExpandAnimator;
+ private ValueAnimator mContractAnimator;
private GradientDrawable mBackground;
private RippleDrawable mRipple;
private GradientDrawable mRippleMask;
@@ -55,18 +58,28 @@
mMargin = context.getResources().getDimensionPixelSize(R.dimen.num_pad_key_margin);
// Actual values will be updated later, usually during an onLayout() call
- mAnimator = ValueAnimator.ofFloat(0f);
- mAnimator.setDuration(100);
- mAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
- mAnimator.setRepeatMode(ValueAnimator.REVERSE);
- mAnimator.setRepeatCount(1);
- mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ mAnimator = new AnimatorSet();
+ mExpandAnimator = ValueAnimator.ofFloat(0f, 1f);
+ mExpandAnimator.setDuration(50);
+ mExpandAnimator.setInterpolator(Interpolators.LINEAR);
+ mExpandAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator anim) {
mBackground.setCornerRadius((float) anim.getAnimatedValue());
mRippleMask.setCornerRadius((float) anim.getAnimatedValue());
}
});
+ mContractAnimator = ValueAnimator.ofFloat(1f, 0f);
+ mContractAnimator.setStartDelay(33);
+ mContractAnimator.setDuration(417);
+ mContractAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mContractAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ public void onAnimationUpdate(ValueAnimator anim) {
+ mBackground.setCornerRadius((float) anim.getAnimatedValue());
+ mRippleMask.setCornerRadius((float) anim.getAnimatedValue());
+ }
+ });
+ mAnimator.playSequentially(mExpandAnimator, mContractAnimator);
}
void updateMargin(ViewGroup.MarginLayoutParams lp) {
@@ -77,7 +90,8 @@
float startRadius = height / 2f;
float endRadius = height / 4f;
mBackground.setCornerRadius(startRadius);
- mAnimator.setFloatValues(startRadius, endRadius);
+ mExpandAnimator.setFloatValues(startRadius, endRadius);
+ mContractAnimator.setFloatValues(endRadius, startRadius);
}
void start() {
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
index 5db3349..1635c49 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
@@ -17,6 +17,7 @@
import android.content.Context;
import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.VectorDrawable;
import android.util.AttributeSet;
@@ -24,6 +25,8 @@
import android.view.MotionEvent;
import android.view.ViewGroup;
+import androidx.annotation.Nullable;
+
import com.android.settingslib.Utils;
import com.android.systemui.R;
@@ -32,13 +35,19 @@
*/
public class NumPadButton extends AlphaOptimizedImageButton {
+ @Nullable
private NumPadAnimator mAnimator;
public NumPadButton(Context context, AttributeSet attrs) {
super(context, attrs);
- mAnimator = new NumPadAnimator(context, (LayerDrawable) getBackground(),
- attrs.getStyleAttribute());
+ Drawable background = getBackground();
+ if (background instanceof LayerDrawable) {
+ mAnimator = new NumPadAnimator(context, (LayerDrawable) background,
+ attrs.getStyleAttribute());
+ } else {
+ mAnimator = null;
+ }
}
@Override
@@ -93,9 +102,11 @@
* By default, the new layout will be enabled. Invoking will revert to the old style
*/
public void disableNewLayout() {
- mAnimator = null;
- ContextThemeWrapper ctw = new ContextThemeWrapper(getContext(), R.style.NumPadKey);
- setBackground(getContext().getResources().getDrawable(
- R.drawable.ripple_drawable_pin, ctw.getTheme()));
+ if (mAnimator != null) {
+ mAnimator = null;
+ ContextThemeWrapper ctw = new ContextThemeWrapper(getContext(), R.style.NumPadKey);
+ setBackground(getContext().getResources().getDrawable(
+ R.drawable.ripple_drawable_pin, ctw.getTheme()));
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index e6a9d4f..96fceee 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.PowerManager;
import android.os.SystemClock;
@@ -31,6 +32,8 @@
import android.view.accessibility.AccessibilityManager;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.internal.widget.LockPatternUtils;
import com.android.settingslib.Utils;
import com.android.systemui.R;
@@ -48,6 +51,7 @@
private int mTextViewResId;
private PasswordTextView mTextView;
+ @Nullable
private NumPadAnimator mAnimator;
private View.OnClickListener mListener = new View.OnClickListener() {
@@ -127,8 +131,13 @@
setContentDescription(mDigitText.getText().toString());
- mAnimator = new NumPadAnimator(context, (LayerDrawable) getBackground(),
- R.style.NumPadKey);
+ Drawable background = getBackground();
+ if (background instanceof LayerDrawable) {
+ mAnimator = new NumPadAnimator(context, (LayerDrawable) background,
+ R.style.NumPadKey);
+ } else {
+ mAnimator = null;
+ }
}
/**
@@ -136,10 +145,12 @@
*/
public void disableNewLayout() {
findViewById(R.id.klondike_text).setVisibility(View.VISIBLE);
- mAnimator = null;
- ContextThemeWrapper ctw = new ContextThemeWrapper(getContext(), R.style.NumPadKey);
- setBackground(getContext().getResources().getDrawable(
- R.drawable.ripple_drawable_pin, ctw.getTheme()));
+ if (mAnimator != null) {
+ mAnimator = null;
+ ContextThemeWrapper ctw = new ContextThemeWrapper(getContext(), R.style.NumPadKey);
+ setBackground(getContext().getResources().getDrawable(
+ R.drawable.ripple_drawable_pin, ctw.getTheme()));
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
index 5ffc283..b80f8bd 100644
--- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
@@ -121,9 +121,19 @@
public PasswordTextView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- setFocusableInTouchMode(true);
- setFocusable(true);
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PasswordTextView);
+ TypedArray a = context.obtainStyledAttributes(attrs, android.R.styleable.View);
+ try {
+ // If defined, use the provided values. If not, set them to true by default.
+ boolean isFocusable = a.getBoolean(android.R.styleable.View_focusable,
+ /* defValue= */ true);
+ boolean isFocusableInTouchMode = a.getBoolean(
+ android.R.styleable.View_focusableInTouchMode, /* defValue= */ true);
+ setFocusable(isFocusable);
+ setFocusableInTouchMode(isFocusableInTouchMode);
+ } finally {
+ a.recycle();
+ }
+ a = context.obtainStyledAttributes(attrs, R.styleable.PasswordTextView);
try {
mTextHeightRaw = a.getInt(R.styleable.PasswordTextView_scaledTextSize, 0);
mGravity = a.getInt(R.styleable.PasswordTextView_android_gravity, Gravity.CENTER);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index 1bf7474..c051b69 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -53,12 +53,18 @@
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.recents.Recents;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
+import com.android.systemui.util.Assert;
import java.util.Locale;
import javax.inject.Inject;
+import dagger.Lazy;
+
/**
* Class to register system actions with accessibility framework.
*/
@@ -128,23 +134,39 @@
public static final int SYSTEM_ACTION_ID_ACCESSIBILITY_SHORTCUT =
AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT; // 13
+ public static final int SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE =
+ AccessibilityService.GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE; // 15
+
private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
- private SystemActionsBroadcastReceiver mReceiver;
+ private final SystemActionsBroadcastReceiver mReceiver;
private Locale mLocale;
- private AccessibilityManager mA11yManager;
+ private final AccessibilityManager mA11yManager;
+ private final Lazy<StatusBar> mStatusBar;
+ private final NotificationShadeWindowController mNotificationShadeController;
+ private final StatusBarWindowCallback mNotificationShadeCallback;
+ private boolean mDismissNotificationShadeActionRegistered;
@Inject
- public SystemActions(Context context) {
+ public SystemActions(Context context,
+ NotificationShadeWindowController notificationShadeController,
+ Lazy<StatusBar> statusBar) {
super(context);
mReceiver = new SystemActionsBroadcastReceiver();
mLocale = mContext.getResources().getConfiguration().getLocales().get(0);
mA11yManager = (AccessibilityManager) mContext.getSystemService(
Context.ACCESSIBILITY_SERVICE);
+ mNotificationShadeController = notificationShadeController;
+ // Saving in instance variable since to prevent GC since
+ // NotificationShadeWindowController.registerCallback() only keeps weak references.
+ mNotificationShadeCallback = (keyguardShowing, keyguardOccluded, bouncerShowing) ->
+ registerOrUnregisterDismissNotificationShadeAction();
+ mStatusBar = statusBar;
}
@Override
public void start() {
+ mNotificationShadeController.registerCallback(mNotificationShadeCallback);
mContext.registerReceiverForAllUsers(
mReceiver,
mReceiver.createIntentFilter(),
@@ -210,6 +232,32 @@
mA11yManager.registerSystemAction(actionTakeScreenshot, SYSTEM_ACTION_ID_TAKE_SCREENSHOT);
mA11yManager.registerSystemAction(
actionAccessibilityShortcut, SYSTEM_ACTION_ID_ACCESSIBILITY_SHORTCUT);
+ registerOrUnregisterDismissNotificationShadeAction();
+ }
+
+ private void registerOrUnregisterDismissNotificationShadeAction() {
+ Assert.isMainThread();
+
+ // Saving state in instance variable since this callback is called quite often to avoid
+ // binder calls
+ StatusBar statusBar = mStatusBar.get();
+ if (statusBar.isPanelExpanded() && !statusBar.isKeyguardShowing()) {
+ if (!mDismissNotificationShadeActionRegistered) {
+ mA11yManager.registerSystemAction(
+ createRemoteAction(
+ R.string.accessibility_system_action_dismiss_notification_shade,
+ SystemActionsBroadcastReceiver
+ .INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE),
+ SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE);
+ mDismissNotificationShadeActionRegistered = true;
+ }
+ } else {
+ if (mDismissNotificationShadeActionRegistered) {
+ mA11yManager.unregisterSystemAction(
+ SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE);
+ mDismissNotificationShadeActionRegistered = false;
+ }
+ }
}
/**
@@ -261,10 +309,15 @@
R.string.accessibility_system_action_on_screen_a11y_shortcut_chooser_label;
intent = SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER;
break;
- case SYSTEM_ACTION_ID_ACCESSIBILITY_SHORTCUT:
+ case SYSTEM_ACTION_ID_ACCESSIBILITY_SHORTCUT:
labelId = R.string.accessibility_system_action_hardware_a11y_shortcut_label;
intent = SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_SHORTCUT;
break;
+ case SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE:
+ labelId = R.string.accessibility_system_action_dismiss_notification_shade;
+ intent = SystemActionsBroadcastReceiver
+ .INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE;
+ break;
default:
return;
}
@@ -369,6 +422,10 @@
mA11yManager.performAccessibilityShortcut();
}
+ private void handleAccessibilityDismissNotificationShade() {
+ mStatusBar.get().animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, false /* force */);
+ }
+
private class SystemActionsBroadcastReceiver extends BroadcastReceiver {
private static final String INTENT_ACTION_BACK = "SYSTEM_ACTION_BACK";
private static final String INTENT_ACTION_HOME = "SYSTEM_ACTION_HOME";
@@ -384,6 +441,8 @@
"SYSTEM_ACTION_ACCESSIBILITY_BUTTON_MENU";
private static final String INTENT_ACTION_ACCESSIBILITY_SHORTCUT =
"SYSTEM_ACTION_ACCESSIBILITY_SHORTCUT";
+ private static final String INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE =
+ "SYSTEM_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE";
private PendingIntent createPendingIntent(Context context, String intentAction) {
switch (intentAction) {
@@ -397,7 +456,8 @@
case INTENT_ACTION_TAKE_SCREENSHOT:
case INTENT_ACTION_ACCESSIBILITY_BUTTON:
case INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER:
- case INTENT_ACTION_ACCESSIBILITY_SHORTCUT: {
+ case INTENT_ACTION_ACCESSIBILITY_SHORTCUT:
+ case INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE: {
Intent intent = new Intent(intentAction);
intent.setPackage(context.getPackageName());
return PendingIntent.getBroadcast(context, 0, intent,
@@ -422,6 +482,7 @@
intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_BUTTON);
intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER);
intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_SHORTCUT);
+ intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE);
return intentFilter;
}
@@ -473,6 +534,10 @@
handleAccessibilityShortcut();
break;
}
+ case INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE: {
+ handleAccessibilityDismissNotificationShade();
+ break;
+ }
default:
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
index d80e085..727a40d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
@@ -17,7 +17,6 @@
package com.android.systemui.biometrics;
import android.content.Context;
-import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
@@ -37,11 +36,10 @@
public class UdfpsEnrollDrawable extends UdfpsDrawable {
private static final String TAG = "UdfpsAnimationEnroll";
- private static final float SHADOW_RADIUS = 5.f;
- static final float PROGRESS_BAR_RADIUS = 140.f;
+ static final float PROGRESS_BAR_RADIUS = 180.f;
@NonNull private final Drawable mMovingTargetFpIcon;
- @NonNull private final Paint mSensorPaint;
+ @NonNull private final Paint mSensorOutlinePaint;
@NonNull private final Paint mBlueFill;
@NonNull private final Paint mBlueStroke;
@@ -51,11 +49,11 @@
UdfpsEnrollDrawable(@NonNull Context context) {
super(context);
- mSensorPaint = new Paint(0 /* flags */);
- mSensorPaint.setAntiAlias(true);
- mSensorPaint.setColor(Color.WHITE);
- mSensorPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, Color.BLACK);
- mSensorPaint.setStyle(Paint.Style.FILL);
+ mSensorOutlinePaint = new Paint(0 /* flags */);
+ mSensorOutlinePaint.setAntiAlias(true);
+ mSensorOutlinePaint.setColor(mContext.getColor(R.color.udfps_enroll_icon));
+ mSensorOutlinePaint.setStyle(Paint.Style.STROKE);
+ mSensorOutlinePaint.setStrokeWidth(2.f);
mBlueFill = new Paint(0 /* flags */);
mBlueFill.setAntiAlias(true);
@@ -98,18 +96,15 @@
return;
}
- final boolean isNightMode = (mContext.getResources().getConfiguration().uiMode
- & Configuration.UI_MODE_NIGHT_YES) != 0;
- if (!isNightMode) {
- if (mSensorRect != null) {
- canvas.drawOval(mSensorRect, mSensorPaint);
- }
+ if (mSensorRect != null) {
+ canvas.drawOval(mSensorRect, mSensorOutlinePaint);
}
mFingerprintDrawable.draw(canvas);
// Draw moving target
if (mEnrollHelper.isCenterEnrollmentComplete()) {
mFingerprintDrawable.setAlpha(mAlpha == 255 ? 64 : mAlpha);
+ mSensorOutlinePaint.setAlpha(mAlpha == 255 ? 64 : mAlpha);
canvas.save();
final PointF point = mEnrollHelper.getNextGuidedEnrollmentPoint();
@@ -123,13 +118,14 @@
canvas.restore();
} else {
mFingerprintDrawable.setAlpha(mAlpha);
+ mSensorOutlinePaint.setAlpha(mAlpha);
}
}
@Override
public void setAlpha(int alpha) {
super.setAlpha(alpha);
- mSensorPaint.setAlpha(alpha);
+ mSensorOutlinePaint.setAlpha(alpha);
mBlueFill.setAlpha(alpha);
mBlueStroke.setAlpha(alpha);
invalidateSelf();
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index efb7992..45cefcc 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -31,6 +31,7 @@
import com.android.systemui.dagger.qualifiers.TestHarness;
import com.android.systemui.dock.DockManager;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.sensors.ThresholdSensor;
import java.io.FileDescriptor;
@@ -67,6 +68,7 @@
private final SingleTapClassifier mSingleTapClassifier;
private final DoubleTapClassifier mDoubleTapClassifier;
private final HistoryTracker mHistoryTracker;
+ private final KeyguardStateController mKeyguardStateController;
private final boolean mTestHarness;
private final MetricsLogger mMetricsLogger;
private int mIsFalseTouchCalls;
@@ -113,7 +115,8 @@
DockManager dockManager, MetricsLogger metricsLogger,
@Named(BRIGHT_LINE_GESTURE_CLASSIFERS) Set<FalsingClassifier> classifiers,
SingleTapClassifier singleTapClassifier, DoubleTapClassifier doubleTapClassifier,
- HistoryTracker historyTracker, @TestHarness boolean testHarness) {
+ HistoryTracker historyTracker, KeyguardStateController keyguardStateController,
+ @TestHarness boolean testHarness) {
mDataProvider = falsingDataProvider;
mDockManager = dockManager;
mMetricsLogger = metricsLogger;
@@ -121,6 +124,7 @@
mSingleTapClassifier = singleTapClassifier;
mDoubleTapClassifier = doubleTapClassifier;
mHistoryTracker = historyTracker;
+ mKeyguardStateController = keyguardStateController;
mTestHarness = testHarness;
mDataProvider.addSessionListener(mSessionListener);
@@ -134,6 +138,10 @@
@Override
public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
+ if (skipFalsing()) {
+ return false;
+ }
+
boolean result;
mDataProvider.setInteractionType(interactionType);
@@ -195,6 +203,10 @@
@Override
public boolean isFalseTap(boolean robustCheck, double falsePenalty) {
+ if (skipFalsing()) {
+ return false;
+ }
+
FalsingClassifier.Result singleTapResult =
mSingleTapClassifier.isTap(mDataProvider.getRecentMotionEvents());
mPriorResults = Collections.singleton(singleTapResult);
@@ -233,6 +245,10 @@
@Override
public boolean isFalseDoubleTap() {
+ if (skipFalsing()) {
+ return false;
+ }
+
FalsingClassifier.Result result = mDoubleTapClassifier.classifyGesture();
mPriorResults = Collections.singleton(result);
if (result.isFalse()) {
@@ -246,6 +262,10 @@
return result.isFalse();
}
+ private boolean skipFalsing() {
+ return !mKeyguardStateController.isShowing();
+ }
+
@Override
public void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) {
// TODO: some of these classifiers might allow us to abort early, meaning we don't have to
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index e090006..b359860 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -27,6 +27,7 @@
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.sensors.ThresholdSensor;
import com.android.systemui.util.time.SystemClock;
@@ -48,6 +49,7 @@
private final HistoryTracker mHistoryTracker;
private final ProximitySensor mProximitySensor;
private final StatusBarStateController mStatusBarStateController;
+ private final KeyguardStateController mKeyguardStateController;
private final SystemClock mSystemClock;
private int mState;
@@ -87,13 +89,14 @@
FalsingCollectorImpl(FalsingDataProvider falsingDataProvider, FalsingManager falsingManager,
KeyguardUpdateMonitor keyguardUpdateMonitor, HistoryTracker historyTracker,
ProximitySensor proximitySensor, StatusBarStateController statusBarStateController,
- SystemClock systemClock) {
+ KeyguardStateController keyguardStateController, SystemClock systemClock) {
mFalsingDataProvider = falsingDataProvider;
mFalsingManager = falsingManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mHistoryTracker = historyTracker;
mProximitySensor = proximitySensor;
mStatusBarStateController = statusBarStateController;
+ mKeyguardStateController = keyguardStateController;
mSystemClock = systemClock;
@@ -255,6 +258,10 @@
@Override
public void onTouchEvent(MotionEvent ev) {
+ if (!mKeyguardStateController.isShowing()) {
+ avoidGesture();
+ return;
+ }
// We delay processing down events to see if another component wants to process them.
// If #avoidGesture is called after a MotionEvent.ACTION_DOWN, all following motion events
// will be ignored by the collector until another MotionEvent.ACTION_DOWN is passed in.
@@ -276,8 +283,8 @@
@Override
public void avoidGesture() {
+ mAvoidGesture = true;
if (mPendingDownEvent != null) {
- mAvoidGesture = true;
mPendingDownEvent.recycle();
mPendingDownEvent = null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLogger.kt
new file mode 100644
index 0000000..3bfdcae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLogger.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.controls
+
+import android.service.controls.DeviceTypes.DeviceType
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.controls.ui.ControlViewHolder
+
+/**
+ * Interface for logging UI events related to controls
+ */
+interface ControlsMetricsLogger {
+
+ /**
+ * Assign a new instance id for this controls session, defined as when the controls area is
+ * made visible to when it is closed.
+ */
+ fun assignInstanceId()
+
+ fun touch(cvh: ControlViewHolder, isLocked: Boolean) {
+ log(ControlsEvents.CONTROL_TOUCH.id, cvh.deviceType, cvh.uid, isLocked)
+ }
+
+ fun drag(cvh: ControlViewHolder, isLocked: Boolean) {
+ log(ControlsEvents.CONTROL_DRAG.id, cvh.deviceType, cvh.uid, isLocked)
+ }
+
+ fun longPress(cvh: ControlViewHolder, isLocked: Boolean) {
+ log(ControlsEvents.CONTROL_LONG_PRESS.id, cvh.deviceType, cvh.uid, isLocked)
+ }
+
+ fun refreshBegin(uid: Int, isLocked: Boolean) {
+ assignInstanceId()
+ log(ControlsEvents.CONTROL_REFRESH_BEGIN.id, 0, uid, isLocked)
+ }
+
+ fun refreshEnd(cvh: ControlViewHolder, isLocked: Boolean) {
+ log(ControlsEvents.CONTROL_REFRESH_END.id, cvh.deviceType, cvh.uid, isLocked)
+ }
+
+ /**
+ * Logs a controls-related event
+ *
+ * @param eventId Main UIEvent to capture
+ * @param deviceType One of {@link android.service.controls.DeviceTypes}
+ * @param packageName Package name of the service that receives the request
+ * @param isLocked Is the device locked at the start of the action?
+ */
+ fun log(
+ eventId: Int,
+ @DeviceType deviceType: Int,
+ uid: Int,
+ isLocked: Boolean
+ )
+
+ private enum class ControlsEvents(val metricId: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "User touched a control")
+ CONTROL_TOUCH(714),
+
+ @UiEvent(doc = "User dragged a control")
+ CONTROL_DRAG(713),
+
+ @UiEvent(doc = "User long-pressed a control")
+ CONTROL_LONG_PRESS(715),
+
+ @UiEvent(doc = "User has opened controls, and a state refresh has begun")
+ CONTROL_REFRESH_BEGIN(716),
+
+ @UiEvent(doc = "User has opened controls, and a state refresh has ended")
+ CONTROL_REFRESH_END(717);
+
+ override fun getId() = metricId
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLoggerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLoggerImpl.kt
new file mode 100644
index 0000000..c165632
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLoggerImpl.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.controls
+
+import android.service.controls.DeviceTypes.DeviceType
+
+import com.android.internal.logging.InstanceIdSequence
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shared.system.SysUiStatsLog
+
+import javax.inject.Inject
+
+/**
+ * Implementation for logging UI events related to controls
+ */
+@SysUISingleton
+class ControlsMetricsLoggerImpl @Inject constructor() : ControlsMetricsLogger {
+
+ companion object {
+ private const val INSTANCE_ID_MAX = 1 shl 13
+ }
+
+ private val instanceIdSequence = InstanceIdSequence(INSTANCE_ID_MAX)
+ private var instanceId = 0
+
+ override fun assignInstanceId() {
+ instanceId = instanceIdSequence.newInstanceId().id
+ }
+
+ /**
+ * {@see ControlsMetricsLogger#log}
+ */
+ override fun log(
+ eventId: Int,
+ @DeviceType deviceType: Int,
+ uid: Int,
+ isLocked: Boolean
+ ) {
+ SysUiStatsLog.write(
+ SysUiStatsLog.DEVICE_CONTROL_CHANGED,
+ eventId,
+ instanceId,
+ deviceType,
+ uid,
+ isLocked
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
index ed625de..a165bb2 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
@@ -18,6 +18,8 @@
import android.app.Activity
import android.content.pm.PackageManager
+import com.android.systemui.controls.ControlsMetricsLogger
+import com.android.systemui.controls.ControlsMetricsLoggerImpl
import com.android.systemui.controls.controller.ControlsBindingController
import com.android.systemui.controls.controller.ControlsBindingControllerImpl
import com.android.systemui.controls.controller.ControlsController
@@ -80,6 +82,9 @@
abstract fun provideUiController(controller: ControlsUiControllerImpl): ControlsUiController
@Binds
+ abstract fun provideMetricsLogger(logger: ControlsMetricsLoggerImpl): ControlsMetricsLogger
+
+ @Binds
abstract fun provideControlActionCoordinator(
coordinator: ControlActionCoordinatorImpl
): ControlActionCoordinator
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index 58a5981..477c220 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -18,7 +18,6 @@
import android.annotation.MainThread
import android.app.Dialog
-import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
@@ -33,6 +32,7 @@
import android.view.HapticFeedbackConstants
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.globalactions.GlobalActionsComponent
@@ -54,13 +54,15 @@
private val globalActionsComponent: GlobalActionsComponent,
private val taskViewFactory: Optional<TaskViewFactory>,
private val broadcastDispatcher: BroadcastDispatcher,
- private val lazyUiController: Lazy<ControlsUiController>
+ private val lazyUiController: Lazy<ControlsUiController>,
+ private val controlsMetricsLogger: ControlsMetricsLogger
) : ControlActionCoordinator {
private var dialog: Dialog? = null
private val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
private var pendingAction: Action? = null
private var actionsInProgress = mutableSetOf<String>()
-
+ private val isLocked: Boolean
+ get() = !keyguardStateController.isUnlocked()
override var activityContext: Context? = null
companion object {
@@ -73,6 +75,7 @@
}
override fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) {
+ controlsMetricsLogger.touch(cvh, isLocked)
bouncerOrRun(createAction(cvh.cws.ci.controlId, {
cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
cvh.action(BooleanAction(templateId, !isChecked))
@@ -80,6 +83,7 @@
}
override fun touch(cvh: ControlViewHolder, templateId: String, control: Control) {
+ controlsMetricsLogger.touch(cvh, isLocked)
val blockable = cvh.usePanel()
bouncerOrRun(createAction(cvh.cws.ci.controlId, {
cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
@@ -100,12 +104,14 @@
}
override fun setValue(cvh: ControlViewHolder, templateId: String, newValue: Float) {
+ controlsMetricsLogger.drag(cvh, isLocked)
bouncerOrRun(createAction(cvh.cws.ci.controlId, {
cvh.action(FloatAction(templateId, newValue))
}, false /* blockable */))
}
override fun longPress(cvh: ControlViewHolder) {
+ controlsMetricsLogger.longPress(cvh, isLocked)
bouncerOrRun(createAction(cvh.cws.ci.controlId, {
// Long press snould only be called when there is valid control state, otherwise ignore
cvh.cws.control?.let {
@@ -116,7 +122,7 @@
}
override fun runPendingAction(controlId: String) {
- if (!keyguardStateController.isUnlocked()) return
+ if (isLocked) return
if (pendingAction?.controlId == controlId) {
pendingAction?.invoke()
pendingAction = null
@@ -141,28 +147,17 @@
@VisibleForTesting
fun bouncerOrRun(action: Action) {
if (keyguardStateController.isShowing()) {
- var closeDialog = !keyguardStateController.isUnlocked()
- if (closeDialog) {
+ if (isLocked) {
context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
// pending actions will only run after the control state has been refreshed
pendingAction = action
}
-
+ val wasLocked = isLocked
activityStarter.dismissKeyguardThenExecute({
Log.d(ControlsUiController.TAG, "Device unlocked, invoking controls action")
- if (closeDialog) {
- activityContext?.let {
- val i = Intent().apply {
- component = ComponentName(context, ControlsActivity::class.java)
- addFlags(
- Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
- putExtra(ControlsUiController.BACK_TO_GLOBAL_ACTIONS, false)
- }
- it.startActivity(i)
- } ?: run {
- globalActionsComponent.handleShowGlobalActionsMenu()
- }
+ if (wasLocked && activityContext == null) {
+ globalActionsComponent.handleShowGlobalActionsMenu()
} else {
action.invoke()
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 9d92a40..3e02890 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -49,6 +49,7 @@
import com.android.internal.graphics.ColorUtils
import com.android.systemui.Interpolators
import com.android.systemui.R
+import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.util.concurrency.DelayableExecutor
import kotlin.reflect.KClass
@@ -63,7 +64,9 @@
val controlsController: ControlsController,
val uiExecutor: DelayableExecutor,
val bgExecutor: DelayableExecutor,
- val controlActionCoordinator: ControlActionCoordinator
+ val controlActionCoordinator: ControlActionCoordinator,
+ val controlsMetricsLogger: ControlsMetricsLogger,
+ val uid: Int
) {
companion object {
@@ -141,7 +144,7 @@
status.setSelected(true)
}
- fun bindData(cws: ControlWithState) {
+ fun bindData(cws: ControlWithState, isLocked: Boolean) {
// If an interaction is in progress, the update may visually interfere with the action the
// action the user wants to make. Don't apply the update, and instead assume a new update
// will coming from when the user interaction is complete.
@@ -171,10 +174,16 @@
controlActionCoordinator.runPendingAction(cws.ci.controlId)
}
+ val wasLoading = isLoading
isLoading = false
behavior = bindBehavior(behavior,
findBehaviorClass(controlStatus, controlTemplate, deviceType))
updateContentDescription()
+
+ // Only log one event per control, at the moment we have determined that the control
+ // switched from the loading to done state
+ val doneLoading = wasLoading && !isLoading
+ if (doneLoading) controlsMetricsLogger.refreshEnd(this, isLocked)
}
fun actionResponse(@ControlAction.ResponseResult response: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 0f7f48f..d08882b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -43,6 +43,7 @@
import android.widget.Space
import android.widget.TextView
import com.android.systemui.R
+import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.CustomIconCache
import com.android.systemui.controls.controller.ControlInfo
@@ -58,6 +59,7 @@
import com.android.systemui.globalactions.GlobalActionsPopupMenu
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.phone.ShadeController
+import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.DelayableExecutor
import dagger.Lazy
import java.text.Collator
@@ -77,7 +79,9 @@
val controlActionCoordinator: ControlActionCoordinator,
private val activityStarter: ActivityStarter,
private val shadeController: ShadeController,
- private val iconCache: CustomIconCache
+ private val iconCache: CustomIconCache,
+ private val controlsMetricsLogger: ControlsMetricsLogger,
+ private val keyguardStateController: KeyguardStateController
) : ControlsUiController {
companion object {
@@ -133,7 +137,8 @@
return object : ControlsListingController.ControlsListingCallback {
override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
val lastItems = serviceInfos.map {
- SelectionItem(it.loadLabel(), "", it.loadIcon(), it.componentName)
+ val uid = it.serviceInfo.applicationInfo.uid
+ SelectionItem(it.loadLabel(), "", it.loadIcon(), it.componentName, uid)
}
uiExecutor.execute {
parent.removeAllViews()
@@ -282,8 +287,19 @@
private fun showControlsView(items: List<SelectionItem>) {
controlViewsById.clear()
- createListView()
- createDropDown(items)
+ val itemsByComponent = items.associateBy { it.componentName }
+ val itemsWithStructure = mutableListOf<SelectionItem>()
+ allStructures.mapNotNullTo(itemsWithStructure) {
+ itemsByComponent.get(it.componentName)?.copy(structure = it.structure)
+ }
+ itemsWithStructure.sortWith(localeComparator)
+
+ val selectionItem = findSelectionItem(selectedStructure, itemsWithStructure) ?: items[0]
+
+ controlsMetricsLogger.refreshBegin(selectionItem.uid, !keyguardStateController.isUnlocked())
+
+ createListView(selectionItem)
+ createDropDown(itemsWithStructure, selectionItem)
createMenu()
}
@@ -325,22 +341,13 @@
})
}
- private fun createDropDown(items: List<SelectionItem>) {
+ private fun createDropDown(items: List<SelectionItem>, selected: SelectionItem) {
items.forEach {
RenderInfo.registerComponentIcon(it.componentName, it.icon)
}
- val itemsByComponent = items.associateBy { it.componentName }
- val itemsWithStructure = mutableListOf<SelectionItem>()
- allStructures.mapNotNullTo(itemsWithStructure) {
- itemsByComponent.get(it.componentName)?.copy(structure = it.structure)
- }
- itemsWithStructure.sortWith(localeComparator)
-
- val selectionItem = findSelectionItem(selectedStructure, itemsWithStructure) ?: items[0]
-
var adapter = ItemAdapter(context, R.layout.controls_spinner_item).apply {
- addAll(itemsWithStructure)
+ addAll(items)
}
/*
@@ -349,13 +356,13 @@
* a similar effect
*/
val spinner = parent.requireViewById<TextView>(R.id.app_or_structure_spinner).apply {
- setText(selectionItem.getTitle())
+ setText(selected.getTitle())
// override the default color on the dropdown drawable
(getBackground() as LayerDrawable).getDrawable(0)
.setTint(context.resources.getColor(R.color.control_spinner_dropdown, null))
}
- if (itemsWithStructure.size == 1) {
+ if (items.size == 1) {
spinner.setBackground(null)
return
}
@@ -388,7 +395,7 @@
})
}
- private fun createListView() {
+ private fun createListView(selected: SelectionItem) {
val inflater = LayoutInflater.from(context)
inflater.inflate(R.layout.controls_with_favorites, parent, true)
@@ -421,9 +428,11 @@
controlsController.get(),
uiExecutor,
bgExecutor,
- controlActionCoordinator
+ controlActionCoordinator,
+ controlsMetricsLogger,
+ selected.uid
)
- cvh.bindData(it)
+ cvh.bindData(it, false /* isLocked, will be ignored on initial load */)
controlViewsById.put(key, cvh)
}
}
@@ -528,6 +537,7 @@
}
override fun onRefreshState(componentName: ComponentName, controls: List<Control>) {
+ val isLocked = !keyguardStateController.isUnlocked()
controls.forEach { c ->
controlsById.get(ControlKey(componentName, c.getControlId()))?.let {
Log.d(ControlsUiController.TAG, "onRefreshState() for id: " + c.getControlId())
@@ -536,8 +546,8 @@
val key = ControlKey(componentName, c.getControlId())
controlsById.put(key, cws)
- uiExecutor.execute {
- controlViewsById.get(key)?.bindData(cws)
+ controlViewsById.get(key)?.let {
+ uiExecutor.execute { it.bindData(cws, isLocked) }
}
}
}
@@ -566,7 +576,8 @@
val appName: CharSequence,
val structure: CharSequence,
val icon: Drawable,
- val componentName: ComponentName
+ val componentName: ComponentName,
+ val uid: Int
) {
fun getTitle() = if (structure.isEmpty()) { appName } else { structure }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index a2f96bb..123ccee 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -324,6 +324,7 @@
/** */
@Provides
+ @SysUISingleton
public AlwaysOnDisplayPolicy provideAlwaysOnDisplayPolicy(Context context) {
return new AlwaysOnDisplayPolicy(context);
}
@@ -349,13 +350,6 @@
/** */
@Provides
@SysUISingleton
- public SystemActions providesSystemActions(Context context) {
- return new SystemActions(context);
- }
-
- /** */
- @Provides
- @SysUISingleton
public Choreographer providesChoreographer() {
return Choreographer.getInstance();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 17f7ccf..5cc0e65 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -71,7 +71,7 @@
* @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
*/
private static boolean sEnableRemoteKeyguardAnimation =
- SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);
+ SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, true);
private final KeyguardViewMediator mKeyguardViewMediator;
private final KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher;
@@ -149,9 +149,14 @@
RemoteAnimationTarget[] wallpapers,
RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) {
- // TODO(bc-unlock): Calls KeyguardViewMediator#setOccluded to update the state and
- // run animation.
try {
+ if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE) {
+ mBinder.setOccluded(true /* isOccluded */, true /* animate */);
+ } else if (transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE) {
+ mBinder.setOccluded(false /* isOccluded */, true /* animate */);
+ }
+ // TODO(bc-unlock): Implement occlude/unocclude animation applied on apps,
+ // wallpapers and nonApps.
finishedCallback.onAnimationFinished();
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException");
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 5536237..c3a523f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -35,6 +35,7 @@
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_FORCE_OPAQUE;
import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
@@ -85,6 +86,7 @@
import android.util.Log;
import android.view.Display;
import android.view.Gravity;
+import android.view.HapticFeedbackConstants;
import android.view.IWindowManager;
import android.view.InsetsState.InternalInsetsType;
import android.view.KeyEvent;
@@ -211,7 +213,9 @@
private Locale mLocale;
private int mLayoutDirection;
+ private boolean mAllowForceNavBarHandleOpaque;
private boolean mForceNavBarHandleOpaque;
+ private Optional<Long> mHomeButtonLongPressDurationMs;
private boolean mIsCurrentUserSetup;
/** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
@@ -333,13 +337,23 @@
// If the current user is not yet setup, then don't update any button alphas
return;
}
+ if (QuickStepContract.isLegacyMode(mNavBarMode)) {
+ // Don't allow the bar buttons to be affected by the alpha
+ return;
+ }
+
ButtonDispatcher buttonDispatcher = null;
boolean forceVisible = false;
- if (QuickStepContract.isSwipeUpMode(mNavBarMode)) {
- buttonDispatcher = mNavigationBarView.getBackButton();
- } else if (QuickStepContract.isGesturalMode(mNavBarMode)) {
- forceVisible = mForceNavBarHandleOpaque;
+ if (QuickStepContract.isGesturalMode(mNavBarMode)) {
+ // Disallow home handle animations when in gestural
+ animate = false;
+ forceVisible = mAllowForceNavBarHandleOpaque && mForceNavBarHandleOpaque;
buttonDispatcher = mNavigationBarView.getHomeHandle();
+ if (getBarTransitions() != null) {
+ getBarTransitions().setBackgroundOverrideAlpha(alpha);
+ }
+ } else if (QuickStepContract.isSwipeUpMode(mNavBarMode)) {
+ buttonDispatcher = mNavigationBarView.getBackButton();
}
if (buttonDispatcher != null) {
buttonDispatcher.setVisibility(
@@ -373,6 +387,14 @@
private final Runnable mAutoDim = () -> getBarTransitions().setAutoDim(true);
+ private final Runnable mOnVariableDurationHomeLongClick = () -> {
+ if (onHomeLongClick(mNavigationBarView.getHomeButton().getCurrentView())) {
+ mNavigationBarView.getHomeButton().getCurrentView().performHapticFeedback(
+ HapticFeedbackConstants.LONG_PRESS,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ }
+ };
+
private final ContentObserver mAssistContentObserver = new ContentObserver(
new Handler(Looper.getMainLooper())) {
@Override
@@ -394,6 +416,13 @@
mForceNavBarHandleOpaque = properties.getBoolean(
NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ true);
}
+
+ if (properties.getKeyset().contains(HOME_BUTTON_LONG_PRESS_DURATION_MS)) {
+ mHomeButtonLongPressDurationMs = Optional.of(
+ properties.getLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 0)
+ ).filter(duration -> duration != 0);
+ reconfigureHomeLongClick();
+ }
}
};
@@ -506,10 +535,17 @@
// Respect the latest disabled-flags.
mCommandQueue.recomputeDisableFlags(mDisplayId, false);
+ mAllowForceNavBarHandleOpaque = mContext.getResources().getBoolean(
+ R.bool.allow_force_nav_bar_handle_opaque);
mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI,
NAV_BAR_HANDLE_FORCE_OPAQUE,
/* defaultValue = */ true);
+ mHomeButtonLongPressDurationMs = Optional.of(DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ HOME_BUTTON_LONG_PRESS_DURATION_MS,
+ /* defaultValue = */ 0
+ )).filter(duration -> duration != 0);
DeviceConfig.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener);
@@ -769,6 +805,22 @@
}
}
+ private void reconfigureHomeLongClick() {
+ if (mNavigationBarView == null
+ || mNavigationBarView.getHomeButton().getCurrentView() == null) {
+ return;
+ }
+ if (mHomeButtonLongPressDurationMs.isPresent()) {
+ mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(false);
+ mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(false);
+ mNavigationBarView.getHomeButton().setOnLongClickListener(null);
+ } else {
+ mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(true);
+ mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(true);
+ mNavigationBarView.getHomeButton().setOnLongClickListener(this::onHomeLongClick);
+ }
+ }
+
private int deltaRotation(int oldRotation, int newRotation) {
int delta = newRotation - oldRotation;
if (delta < 0) delta += 4;
@@ -779,6 +831,7 @@
pw.println("NavigationBar (displayId=" + mDisplayId + "):");
pw.println(" mStartingQuickSwitchRotation=" + mStartingQuickSwitchRotation);
pw.println(" mCurrentRotation=" + mCurrentRotation);
+ pw.println(" mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs);
if (mNavigationBarView != null) {
pw.println(" mNavigationBarWindowState="
@@ -1108,7 +1161,8 @@
ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
homeButton.setOnTouchListener(this::onHomeTouch);
- homeButton.setOnLongClickListener(this::onHomeLongClick);
+
+ reconfigureHomeLongClick();
ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
accessibilityButton.setOnClickListener(this::onAccessibilityClick);
@@ -1118,7 +1172,8 @@
updateScreenPinningGestures();
}
- private boolean onHomeTouch(View v, MotionEvent event) {
+ @VisibleForTesting
+ boolean onHomeTouch(View v, MotionEvent event) {
if (mHomeBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
return true;
}
@@ -1138,9 +1193,13 @@
return true;
}
}
+ mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> {
+ mHandler.postDelayed(mOnVariableDurationHomeLongClick, longPressDuration);
+ });
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
+ mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
mStatusBarLazy.get().awakenDreams();
break;
}
@@ -1468,6 +1527,12 @@
@Override
public void onNavigationModeChanged(int mode) {
mNavBarMode = mode;
+ if (!QuickStepContract.isGesturalMode(mode)) {
+ // Reset the override alpha
+ if (getBarTransitions() != null) {
+ getBarTransitions().setBackgroundOverrideAlpha(1f);
+ }
+ }
updateScreenPinningGestures();
if (!canShowSecondaryHandle()) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
index 61e1d61..fbc7c92 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
@@ -36,6 +36,7 @@
import com.android.systemui.statusbar.phone.BarTransitions;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -135,6 +136,10 @@
mBarBackground.setFrame(frame);
}
+ void setBackgroundOverrideAlpha(float alpha) {
+ mBarBackground.setOverrideAlpha(alpha);
+ }
+
@Override
protected boolean isLightsOut(int mode) {
return super.isLightsOut(mode) || (mAllowAutoDimWallpaperNotVisible && mAutoDim
@@ -230,4 +235,17 @@
public void removeDarkIntensityListener(DarkIntensityListener listener) {
mDarkIntensityListeners.remove(listener);
}
+
+ public void dump(PrintWriter pw) {
+ pw.println("NavigationBarTransitions:");
+ pw.println(" mMode: " + getMode());
+ pw.println(" mAlwaysOpaque: " + isAlwaysOpaque());
+ pw.println(" mAllowAutoDimWallpaperNotVisible: " + mAllowAutoDimWallpaperNotVisible);
+ pw.println(" mWallpaperVisible: " + mWallpaperVisible);
+ pw.println(" mLightsOut: " + mLightsOut);
+ pw.println(" mAutoDim: " + mAutoDim);
+ pw.println(" bg overrideAlpha: " + mBarBackground.getOverrideAlpha());
+ pw.println(" bg color: " + mBarBackground.getColor());
+ pw.println(" bg frame: " + mBarBackground.getFrame());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 148c665..091b17d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -1158,6 +1158,8 @@
int frameHeight = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.navigation_bar_frame_height);
mBarTransitions.setBackgroundFrame(new Rect(0, frameHeight - height, w, h));
+ } else {
+ mBarTransitions.setBackgroundFrame(null);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -1331,6 +1333,7 @@
if (mNavigationInflaterView != null) {
mNavigationInflaterView.dump(pw);
}
+ mBarTransitions.dump(pw);
mContextualButtonGroup.dump(pw);
mRecentsOnboarding.dump(pw);
mRegionSamplingHelper.dump(pw);
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 14a3fc0..1a17e61 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -416,7 +416,6 @@
}
} 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);
@@ -480,9 +479,6 @@
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));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index a87bfd8..8951605 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -294,11 +294,6 @@
}
@Override
- public void setBackButtonAlpha(float alpha, boolean animate) {
- setNavBarButtonAlpha(alpha, animate);
- }
-
- @Override
public void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {
if (!verifyCaller("onAssistantProgress")) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index d3065aa..8c21e76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -349,7 +349,9 @@
invalidateOutline();
selectLayout(false /* animate */, mForceSelectNextLayout /* force */);
mForceSelectNextLayout = false;
- updateExpandButtons(mExpandable);
+ // TODO(b/182314698): move this to onMeasure. This requires switching to getMeasuredHeight,
+ // and also requires revisiting all of the logic called earlier in this method.
+ updateExpandButtonsDuringLayout(mExpandable, true /* duringLayout */);
}
@Override
@@ -1344,9 +1346,7 @@
}
ImageView bubbleButton = layout.findViewById(com.android.internal.R.id.bubble_button);
View actionContainer = layout.findViewById(com.android.internal.R.id.actions_container);
- LinearLayout actionContainerLayout =
- layout.findViewById(com.android.internal.R.id.actions_container_layout);
- if (bubbleButton == null || actionContainer == null || actionContainerLayout == null) {
+ if (bubbleButton == null || actionContainer == null) {
return;
}
boolean isPersonWithShortcut =
@@ -1589,6 +1589,10 @@
}
public void updateExpandButtons(boolean expandable) {
+ updateExpandButtonsDuringLayout(expandable, false /* duringLayout */);
+ }
+
+ private void updateExpandButtonsDuringLayout(boolean expandable, boolean duringLayout) {
mExpandable = expandable;
// if the expanded child has the same height as the collapsed one we hide it.
if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
@@ -1602,14 +1606,15 @@
expandable = false;
}
}
+ boolean requestLayout = duringLayout && mIsContentExpandable != expandable;
if (mExpandedChild != null) {
- mExpandedWrapper.updateExpandability(expandable, mExpandClickListener);
+ mExpandedWrapper.updateExpandability(expandable, mExpandClickListener, requestLayout);
}
if (mContractedChild != null) {
- mContractedWrapper.updateExpandability(expandable, mExpandClickListener);
+ mContractedWrapper.updateExpandability(expandable, mExpandClickListener, requestLayout);
}
if (mHeadsUpChild != null) {
- mHeadsUpWrapper.updateExpandability(expandable, mExpandClickListener);
+ mHeadsUpWrapper.updateExpandability(expandable, mExpandClickListener, requestLayout);
}
mIsContentExpandable = expandable;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index fb0fdcc..383bb7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -147,8 +147,11 @@
override fun setRemoteInputVisible(visible: Boolean) =
conversationLayout.showHistoricMessages(visible)
- override fun updateExpandability(expandable: Boolean, onClickListener: View.OnClickListener?) =
- conversationLayout.updateExpandability(expandable, onClickListener)
+ override fun updateExpandability(
+ expandable: Boolean,
+ onClickListener: View.OnClickListener,
+ requestLayout: Boolean
+ ) = conversationLayout.updateExpandability(expandable, onClickListener)
override fun disallowSingleClick(x: Float, y: Float): Boolean {
val isOnExpandButton = expandBtnContainer.visibility == View.VISIBLE &&
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index bdafd23..5a55545 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -261,7 +261,8 @@
}
@Override
- public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {
+ public void updateExpandability(boolean expandable, View.OnClickListener onClickListener,
+ boolean requestLayout) {
mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE);
mExpandButton.setOnClickListener(expandable ? onClickListener : null);
if (mAltExpandTarget != null) {
@@ -273,6 +274,13 @@
if (mNotificationHeader != null) {
mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
}
+ // Unfortunately, the NotificationContentView has to layout its children in order to
+ // determine their heights, and that affects the button visibility. If that happens
+ // (thankfully it is rare) then we need to request layout of the expand button's parent
+ // in order to ensure it gets laid out correctly.
+ if (requestLayout) {
+ mExpandButton.getParent().requestLayout();
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 9ced12d..3a7b461 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -291,8 +291,10 @@
*
* @param expandable should this view be expandable
* @param onClickListener the listener to invoke when the expand affordance is clicked on
+ * @param requestLayout the expandability changed during onLayout, so a requestLayout required
*/
- public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {}
+ public void updateExpandability(boolean expandable, View.OnClickListener onClickListener,
+ boolean requestLayout) {}
/** Set the expanded state on the view wrapper */
public void setExpanded(boolean expanded) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index f65ae0c..1d30736 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -324,7 +324,7 @@
StatusBarNotification notification = mContainingNotification.getEntry().getSbn();
final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
notification.getNotification());
- RemoteViews header = builder.makeNotificationHeader();
+ RemoteViews header = builder.makeNotificationGroupHeader();
if (mNotificationHeader == null) {
mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this);
mNotificationHeader.findViewById(com.android.internal.R.id.expand_button)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index e6731e6..c60bbc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -166,6 +166,7 @@
private int mGradientAlpha;
private int mColor;
+ private float mOverrideAlpha = 1f;
private PorterDuffColorFilter mTintFilter;
private Paint mPaint = new Paint();
@@ -195,6 +196,23 @@
mFrame = frame;
}
+ public void setOverrideAlpha(float overrideAlpha) {
+ mOverrideAlpha = overrideAlpha;
+ invalidateSelf();
+ }
+
+ public float getOverrideAlpha() {
+ return mOverrideAlpha;
+ }
+
+ public int getColor() {
+ return mColor;
+ }
+
+ public Rect getFrame() {
+ return mFrame;
+ }
+
@Override
public void setAlpha(int alpha) {
// noop
@@ -296,11 +314,13 @@
mGradient.setAlpha(mGradientAlpha);
mGradient.draw(canvas);
}
+
if (Color.alpha(mColor) > 0) {
mPaint.setColor(mColor);
if (mTintFilter != null) {
mPaint.setColorFilter(mTintFilter);
}
+ mPaint.setAlpha((int) (Color.alpha(mColor) * mOverrideAlpha));
if (mFrame != null) {
canvas.drawRect(mFrame, mPaint);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index 59c1138..4615877 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -26,6 +26,7 @@
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardConstants;
@@ -143,6 +144,16 @@
openQsUserPanel();
});
+ mView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.ACTION_CLICK,
+ mContext.getString(
+ R.string.accessibility_quick_settings_choose_user_action)));
+ }
+ });
+
updateView(true /* forceUpdate */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 1b6c612..3f4ec85 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -480,7 +480,7 @@
@WMSingleton
@Provides
static StartingWindowController provideStartingWindowController(Context context,
- @ShellAnimationThread ShellExecutor executor, TransactionPool pool) {
+ @ShellSplashscreenThread ShellExecutor executor, TransactionPool pool) {
return new StartingWindowController(context, executor, pool);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index 49ba646..6272277 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -16,13 +16,19 @@
package com.android.keyguard;
+import static android.view.WindowInsets.Type.ime;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+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.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.res.Resources;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.WindowInsetsController;
@@ -33,6 +39,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.FalsingManager;
@@ -84,18 +91,34 @@
@Mock
private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController;
@Mock
+ private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory;
+ @Mock
+ private KeyguardMessageArea mKeyguardMessageArea;
+ @Mock
private ConfigurationController mConfigurationController;
@Mock
private KeyguardViewController mKeyguardViewController;
private FalsingManager mFalsingManager = new FalsingManagerFake();
private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
+ private KeyguardPasswordViewController mKeyguardPasswordViewController;
+ private KeyguardPasswordView mKeyguardPasswordView;
@Before
public void setup() {
when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class)))
.thenReturn(mAdminSecondaryLockScreenController);
when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
+ mKeyguardPasswordView = spy(new KeyguardPasswordView(getContext()));
+ when(mKeyguardPasswordView.getRootView()).thenReturn(mSecurityViewFlipper);
+ when(mKeyguardPasswordView.findViewById(R.id.keyguard_message_area))
+ .thenReturn(mKeyguardMessageArea);
+ when(mKeyguardPasswordView.getWindowInsetsController()).thenReturn(mWindowInsetsController);
+ mKeyguardPasswordViewController = new KeyguardPasswordViewController(
+ (KeyguardPasswordView) mKeyguardPasswordView, mKeyguardUpdateMonitor,
+ SecurityMode.Password, mLockPatternUtils, null,
+ mKeyguardMessageAreaControllerFactory, null, null, null, mock(Resources.class),
+ null);
mKeyguardSecurityContainerController = new KeyguardSecurityContainerController.Factory(
mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
@@ -125,14 +148,13 @@
public void startDisappearAnimation_animatesKeyboard() {
when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
SecurityMode.Password);
- when(mInputViewController.getSecurityMode()).thenReturn(
- SecurityMode.Password);
when(mKeyguardSecurityViewFlipperController.getSecurityView(
eq(SecurityMode.Password), any(KeyguardSecurityCallback.class)))
- .thenReturn(mInputViewController);
- mKeyguardSecurityContainerController.showPrimarySecurityScreen(false /* turningOff */);
+ .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password);
mKeyguardSecurityContainerController.startDisappearAnimation(null);
- verify(mInputViewController).startDisappearAnimation(eq(null));
+ verify(mWindowInsetsController).controlWindowInsetsAnimation(
+ eq(ime()), anyLong(), any(), any(), any());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 1783fa4..104318e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -19,9 +19,6 @@
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -90,13 +87,6 @@
}
@Test
- public void startDisappearAnimation_animatesKeyboard() {
- mKeyguardSecurityContainer.startDisappearAnimation(SecurityMode.Password);
- verify(mWindowInsetsController).controlWindowInsetsAnimation(eq(ime()), anyLong(), any(),
- any(), any());
- }
-
- @Test
public void onMeasure_usesFullWidthWithoutOneHandedMode() {
setUpKeyguard(
/* deviceConfigCanUseOneHandedKeyguard= */false,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index b9d8d27..e35e987 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -195,7 +195,8 @@
when(mFaceSensorProperties.get(anyInt())).thenReturn(new FaceSensorPropertiesInternal(
0 /* id */,
FaceSensorProperties.STRENGTH_STRONG, 1 /* maxTemplatesAllowed */,
- false /* supportsFaceDetection */, true /* supportsSelfIllumination */));
+ false /* supportsFaceDetection */, true /* supportsSelfIllumination */,
+ false /* resetLockoutRequiresChallenge */));
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
index b232850..d015f51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
@@ -35,6 +35,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingDataProvider.GestureCompleteListener;
import com.android.systemui.dock.DockManagerFake;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -71,7 +72,10 @@
private FalsingClassifier mClassifierB;
private final List<MotionEvent> mMotionEventList = new ArrayList<>();
@Mock
- private HistoryTracker mHistoryTracker;;
+ private HistoryTracker mHistoryTracker;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+
private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
private final FalsingClassifier.Result mFalsedResult = FalsingClassifier.Result.falsed(1, "");
@@ -88,9 +92,10 @@
mClassifiers.add(mClassifierA);
mClassifiers.add(mClassifierB);
when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider, mDockManager,
mMetricsLogger, mClassifiers, mSingleTapClassfier, mDoubleTapClassifier,
- mHistoryTracker, false);
+ mHistoryTracker, mKeyguardStateController, false);
ArgumentCaptor<GestureCompleteListener> gestureCompleteListenerCaptor =
@@ -120,7 +125,7 @@
}
@Test
- public void testIsFalseTouch_ClassffiersPass() {
+ public void testIsFalseTouch_ClassifiersPass() {
assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse();
}
@@ -233,4 +238,18 @@
assertThat(mFakeExecutor.numPending()).isEqualTo(0);
}
+
+ @Test
+ public void testNoFalsingUnlocked() {
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
+
+ when(mClassifierA.classifyGesture(anyDouble(), anyDouble())).thenReturn(mFalsedResult);
+ assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse();
+
+ when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mFalsedResult);
+ assertThat(mBrightLineFalsingManager.isFalseTap(false, 0)).isFalse();
+
+ when(mDoubleTapClassifier.classifyGesture()).thenReturn(mFalsedResult);
+ assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isFalse();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
index dc79b88..e6aeee7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
@@ -34,6 +34,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.sensors.ThresholdSensor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -62,16 +63,19 @@
private ProximitySensor mProximitySensor;
@Mock
private SysuiStatusBarStateController mStatusBarStateController;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
mFalsingCollector = new FalsingCollectorImpl(mFalsingDataProvider, mFalsingManager,
mKeyguardUpdateMonitor, mHistoryTracker, mProximitySensor,
- mStatusBarStateController, new FakeSystemClock());
+ mStatusBarStateController, mKeyguardStateController, new FakeSystemClock());
}
@Test
@@ -159,4 +163,20 @@
mFalsingCollector.onTouchEvent(up);
verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
}
+
+ @Test
+ public void testAvoidUnlocked() {
+ MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+ MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
+
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
+
+ // Nothing passed initially
+ mFalsingCollector.onTouchEvent(down);
+ verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
+
+ // Up event would normally flush the up event.
+ mFalsingCollector.onTouchEvent(up);
+ verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
index 9278570..17eb15b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
@@ -19,6 +19,7 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.globalactions.GlobalActionsComponent
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -63,6 +64,8 @@
private lateinit var taskViewFactory: Optional<TaskViewFactory>
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private lateinit var cvh: ControlViewHolder
+ @Mock
+ private lateinit var metricsLogger: ControlsMetricsLogger
companion object {
fun <T> any(): T = Mockito.any<T>()
@@ -86,7 +89,8 @@
globalActionsComponent,
taskViewFactory,
getFakeBroadcastDispatcher(),
- lazyUiController
+ lazyUiController,
+ metricsLogger
))
`when`(cvh.cws.ci.controlId).thenReturn(ID)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 2b76f1c..22c553b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -23,6 +23,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
import static com.android.systemui.navigationbar.NavigationBar.NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS;
import static org.junit.Assert.assertEquals;
@@ -31,6 +32,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -44,12 +46,16 @@
import android.hardware.display.DisplayManagerGlobal;
import android.os.Handler;
import android.os.Looper;
+import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.MotionEvent;
import android.view.WindowManager;
import android.view.WindowMetrics;
import android.view.accessibility.AccessibilityManager;
@@ -79,6 +85,7 @@
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.pip.Pip;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -101,6 +108,7 @@
private OverviewProxyService mOverviewProxyService;
private CommandQueue mCommandQueue;
private SysUiState mMockSysUiState;
+ @Mock
private Handler mHandler;
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
@@ -124,12 +132,17 @@
mDependency.injectMockDependency(NavigationBarController.class);
mOverviewProxyService = mDependency.injectMockDependency(OverviewProxyService.class);
TestableLooper.get(this).runWithLooper(() -> {
- mHandler = new Handler();
mNavigationBar = createNavBar(mContext);
mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal);
});
}
+ @After
+ public void tearDown() throws Exception {
+ DeviceConfig.resetToDefaults(
+ Settings.RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_SYSTEMUI);
+ }
+
private void setupSysuiDependency() {
Display display = new Display(DisplayManagerGlobal.getInstance(), EXTERNAL_DISPLAY_ID,
new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
@@ -163,6 +176,32 @@
}
@Test
+ public void testHomeLongPressWithCustomDuration() throws Exception {
+ DeviceConfig.setProperties(
+ new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_SYSTEMUI)
+ .setLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 100)
+ .build());
+ mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null));
+
+ mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain(
+ /*downTime=*/SystemClock.uptimeMillis(),
+ /*eventTime=*/SystemClock.uptimeMillis(),
+ /*action=*/MotionEvent.ACTION_DOWN,
+ 0, 0, 0
+ ));
+ verify(mHandler, times(1)).postDelayed(any(), eq(100L));
+
+ mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain(
+ /*downTime=*/SystemClock.uptimeMillis(),
+ /*eventTime=*/SystemClock.uptimeMillis(),
+ /*action=*/MotionEvent.ACTION_UP,
+ 0, 0, 0
+ ));
+
+ verify(mHandler, times(1)).removeCallbacks(any());
+ }
+
+ @Test
public void testRegisteredWithDispatcher() {
mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null));
verify(mBroadcastDispatcher).registerReceiverWithHandler(
diff --git a/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java b/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
index f8b9309..f0de811 100644
--- a/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
+++ b/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
@@ -360,7 +360,7 @@
try {
mCallback.setProxyPort(port);
} catch (RemoteException e) {
- Log.w(TAG, "Proxy failed to report port to PacProxyInstaller", e);
+ Log.w(TAG, "Proxy failed to report port to PacProxyService", e);
}
}
mPort = port;
@@ -371,7 +371,7 @@
try {
callback.setProxyPort(mPort);
} catch (RemoteException e) {
- Log.w(TAG, "Proxy failed to report port to PacProxyInstaller", e);
+ Log.w(TAG, "Proxy failed to report port to PacProxyService", e);
}
}
mCallback = callback;
diff --git a/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java b/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java
index bdf478d..a8e2622 100644
--- a/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java
+++ b/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java
@@ -30,7 +30,7 @@
private static ProxyServer server = null;
- /** Keep these values up-to-date with PacProxyInstaller.java */
+ /** Keep these values up-to-date with PacProxyService.java */
public static final String KEY_PROXY = "keyProxy";
public static final String HOST = "localhost";
public static final String EXCL_LIST = "";
diff --git a/services/api/current.txt b/services/api/current.txt
index 7e8f7a2..a3e6715 100644
--- a/services/api/current.txt
+++ b/services/api/current.txt
@@ -88,6 +88,14 @@
}
+package com.android.server.am {
+
+ public interface ActivityManagerLocal {
+ method public boolean canStartForegroundService(int, int, @NonNull String);
+ }
+
+}
+
package com.android.server.role {
public interface RoleServicePlatformHelper {
diff --git a/services/api/non-updatable-current.txt b/services/api/non-updatable-current.txt
index 3c72d38..f01c182 100644
--- a/services/api/non-updatable-current.txt
+++ b/services/api/non-updatable-current.txt
@@ -35,6 +35,14 @@
}
+package com.android.server.am {
+
+ public interface ActivityManagerLocal {
+ method public boolean canStartForegroundService(int, int, @NonNull String);
+ }
+
+}
+
package com.android.server.role {
public interface RoleServicePlatformHelper {
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 4b56845..92af080 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -963,7 +963,8 @@
private Set<Association> getAllAssociations(int userId, @Nullable String packageFilter) {
return CollectionUtils.filter(
getAllAssociations(userId),
- a -> Objects.equals(packageFilter, a.getPackageName()));
+ // Null filter == get all associations
+ a -> packageFilter == null || Objects.equals(packageFilter, a.getPackageName()));
}
private Set<Association> getAllAssociations() {
@@ -983,8 +984,10 @@
int userId, @Nullable String packageFilter, @Nullable String addressFilter) {
return CollectionUtils.filter(
getAllAssociations(userId),
- a -> Objects.equals(packageFilter, a.getPackageName())
- && Objects.equals(addressFilter, a.getDeviceMacAddress()));
+ // Null filter == get all associations
+ a -> (packageFilter == null || Objects.equals(packageFilter, a.getPackageName()))
+ && (addressFilter == null
+ || Objects.equals(addressFilter, a.getDeviceMacAddress())));
}
private Set<Association> readAllAssociations(int userId) {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index b00689b..0fe874c 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -81,10 +81,19 @@
out: ["services.core.protolog.json"],
}
+genrule {
+ name: "statslog-art-java-gen",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --java $(out) --module art" +
+ " --javaPackage com.android.internal.art --javaClass ArtStatsLog --worksource",
+ out: ["com/android/internal/art/ArtStatsLog.java"],
+}
+
java_library_static {
name: "services.core.unboosted",
defaults: ["platform_service_defaults"],
srcs: [
+ ":statslog-art-java-gen",
":services.core-sources",
":services.core.protologsrc",
":dumpstate_aidl",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index b4fcaee..688b33e 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -4425,7 +4425,8 @@
break;
}
case EVENT_PROXY_HAS_CHANGED: {
- handleApplyDefaultProxy((ProxyInfo)msg.obj);
+ final Pair<Network, ProxyInfo> arg = (Pair<Network, ProxyInfo>) msg.obj;
+ handleApplyDefaultProxy(arg.second);
break;
}
case EVENT_REGISTER_NETWORK_PROVIDER: {
diff --git a/services/core/java/com/android/server/ContextHubSystemService.java b/services/core/java/com/android/server/ContextHubSystemService.java
index a353519..96ff900 100644
--- a/services/core/java/com/android/server/ContextHubSystemService.java
+++ b/services/core/java/com/android/server/ContextHubSystemService.java
@@ -16,6 +16,8 @@
package com.android.server;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.util.Log;
@@ -51,4 +53,9 @@
publishBinderService(Context.CONTEXTHUB_SERVICE, mContextHubService);
}
}
+
+ @Override
+ public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
+ mContextHubService.onUserChanged();
+ }
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 740a1c1..8b5bb1d 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -962,7 +962,7 @@
}
int delay = DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
- ANR_DELAY_MILLIS_DEVICE_CONFIG_KEY, 0);
+ ANR_DELAY_MILLIS_DEVICE_CONFIG_KEY, 5000);
Slog.v(TAG, "getAnrDelayMillis for " + packageName + ". " + delay + "ms");
return delay;
}
@@ -1791,7 +1791,7 @@
public StorageManagerService(Context context) {
sSelf = this;
mVoldAppDataIsolationEnabled = SystemProperties.getBoolean(
- ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
+ ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
mContext = context;
mResolver = mContext.getContentResolver();
mCallbacks = new Callbacks(FgThread.get().getLooper());
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index ee61067..f566277 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -90,7 +90,12 @@
mCm = mContext.getSystemService(ConnectivityManager.class);
mNetworkProvider = new NetworkProvider(mContext, mHandler.getLooper(),
TEST_NETWORK_PROVIDER_NAME);
- mCm.registerNetworkProvider(mNetworkProvider);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mCm.registerNetworkProvider(mNetworkProvider);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
/**
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 7375523..c360190 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -1076,7 +1076,7 @@
} catch (RuntimeException e) {
// The account manager only throws security exceptions, so let's
// log all others.
- if (!(e instanceof SecurityException)) {
+ if (!(e instanceof SecurityException || e instanceof IllegalArgumentException)) {
Slog.wtf(TAG, "Account Manager Crash", e);
}
throw e;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 68c4a73..14f4d02 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1841,7 +1841,7 @@
ServiceState stracker = r.getTracker();
if (stracker != null) {
stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
- r.lastActivity);
+ SystemClock.uptimeMillis());
}
}
if (alreadyStartedOp) {
@@ -1863,7 +1863,7 @@
ServiceState stracker = r.getTracker();
if (stracker != null) {
stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
- r.lastActivity);
+ SystemClock.uptimeMillis());
}
mAm.mAppOpsService.finishOperation(
AppOpsManager.getToken(mAm.mAppOpsService),
@@ -3765,6 +3765,7 @@
}
}
+ final long now = SystemClock.uptimeMillis();
// Check to see if the service had been started as foreground, but being
// brought down before actually showing a notification. That is not allowed.
if (r.fgRequired) {
@@ -3774,8 +3775,7 @@
r.fgWaiting = false;
ServiceState stracker = r.getTracker();
if (stracker != null) {
- stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
- r.lastActivity);
+ stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(), now);
}
mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
@@ -3834,8 +3834,7 @@
decActiveForegroundAppLocked(smap, r);
ServiceState stracker = r.getTracker();
if (stracker != null) {
- stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
- r.lastActivity);
+ stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(), now);
}
mAm.mAppOpsService.finishOperation(
AppOpsManager.getToken(mAm.mAppOpsService),
@@ -3902,7 +3901,6 @@
}
int memFactor = mAm.mProcessStats.getMemFactorLocked();
- long now = SystemClock.uptimeMillis();
if (r.tracker != null) {
r.tracker.setStarted(false, memFactor, now);
r.tracker.setBound(false, memFactor, now);
@@ -5411,16 +5409,28 @@
}
}
+ boolean canStartForegroundServiceLocked(int callingPid, int callingUid, String callingPackage) {
+ if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {
+ return true;
+ }
+ final @ReasonCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked(
+ callingPackage, callingPid, callingUid, null /* serviceRecord */,
+ false /* allowBackgroundActivityStarts */);
+ final @ReasonCode int allowStartFgs = shouldAllowFgsStartForegroundLocked(
+ allowWhileInUse, callingPid, callingUid, callingPackage, null /* targetService */);
+ return allowStartFgs != REASON_DENIED;
+ }
+
/**
* Should allow while-in-use permissions in FGS or not.
* A typical BG started FGS is not allowed to have while-in-use permissions.
* @param callingPackage caller app's package name.
* @param callingUid caller app's uid.
- * @param r the service to start.
+ * @param targetService the service to start.
* @return {@link ReasonCode}
*/
private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,
- int callingPid, int callingUid, ServiceRecord r,
+ int callingPid, int callingUid, @Nullable ServiceRecord targetService,
boolean allowBackgroundActivityStarts) {
int ret = REASON_DENIED;
@@ -5482,8 +5492,8 @@
}
if (ret == REASON_DENIED) {
- if (r.app != null) {
- ActiveInstrumentation instr = r.app.getActiveInstrumentation();
+ if (targetService != null && targetService.app != null) {
+ ActiveInstrumentation instr = targetService.app.getActiveInstrumentation();
if (instr != null && instr.mHasBackgroundActivityStartsPermission) {
ret = REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
}
@@ -5529,16 +5539,44 @@
private @ReasonCode int shouldAllowFgsStartForegroundLocked(
@ReasonCode int allowWhileInUse, String callingPackage, int callingPid,
int callingUid, Intent intent, ServiceRecord r, boolean allowBackgroundActivityStarts) {
- int ret = allowWhileInUse;
FgsStartTempAllowList.TempFgsAllowListEntry tempAllowListReason =
r.mInfoTempFgsAllowListReason = mAm.isAllowlistedForFgsStartLOSP(callingUid);
+ int ret = shouldAllowFgsStartForegroundLocked(allowWhileInUse, callingPid, callingUid,
+ callingPackage, r);
- final StringBuilder sb = new StringBuilder(64);
final int uidState = mAm.getUidStateLocked(callingUid);
+ final String debugInfo =
+ "[callingPackage: " + callingPackage
+ + "; callingUid: " + callingUid
+ + "; uidState: " + ProcessList.makeProcStateString(uidState)
+ + "; intent: " + intent
+ + "; code:" + reasonCodeToString(ret)
+ + "; tempAllowListReason:<"
+ + (tempAllowListReason == null ? null :
+ (tempAllowListReason.mReason
+ + ",reasonCode:"
+ + reasonCodeToString(tempAllowListReason.mReasonCode)
+ + ",duration:" + tempAllowListReason.mDuration
+ + ",callingUid:" + tempAllowListReason.mCallingUid))
+ + ">"
+ + "; targetSdkVersion:" + r.appInfo.targetSdkVersion
+ + "]";
+ if (!debugInfo.equals(r.mInfoAllowStartForeground)) {
+ r.mLoggedInfoAllowStartForeground = false;
+ r.mInfoAllowStartForeground = debugInfo;
+ }
+ return ret;
+ }
+
+ private @ReasonCode int shouldAllowFgsStartForegroundLocked(@ReasonCode int allowWhileInUse,
+ int callingPid, int callingUid, String callingPackage,
+ @Nullable ServiceRecord targetService) {
+ int ret = allowWhileInUse;
+
if (ret == REASON_DENIED) {
+ final int uidState = mAm.getUidStateLocked(callingUid);
// Is the calling UID at PROCESS_STATE_TOP or above?
if (uidState <= PROCESS_STATE_TOP) {
- sb.append("uidState=").append(uidState);
ret = getReasonCodeFromProcState(uidState);
}
}
@@ -5610,8 +5648,10 @@
// NOTE this should always be the last check.
if (ret == REASON_DENIED) {
- if (isPackageExemptedFromFgsRestriction(r.appInfo.packageName, r.appInfo.uid)
- || isPackageExemptedFromFgsRestriction(callingPackage, callingUid)) {
+ if (isPackageExemptedFromFgsRestriction(callingPackage, callingUid)) {
+ ret = REASON_EXEMPTED_PACKAGE;
+ } else if (targetService != null && isPackageExemptedFromFgsRestriction(
+ targetService.appInfo.packageName, targetService.appInfo.uid)) {
ret = REASON_EXEMPTED_PACKAGE;
}
}
@@ -5624,28 +5664,6 @@
}
}
- final String debugInfo =
- "[callingPackage: " + callingPackage
- + "; callingUid: " + callingUid
- + "; uidState: " + ProcessList.makeProcStateString(uidState)
- + "; intent: " + intent
- + "; code:" + reasonCodeToString(ret)
- + "; tempAllowListReason:<" +
- (tempAllowListReason == null ? null :
- (tempAllowListReason.mReason
- + ",reasonCode:"
- + reasonCodeToString(tempAllowListReason.mReasonCode)
- + ",duration:" + tempAllowListReason.mDuration
- + ",callingUid:" + tempAllowListReason.mCallingUid))
- + ">"
- + "; extra:" + sb.toString()
- + "; targetSdkVersion:" + r.appInfo.targetSdkVersion
- + "]";
- if (!debugInfo.equals(r.mInfoAllowStartForeground)) {
- r.mLoggedInfoAllowStartForeground = false;
- r.mInfoAllowStartForeground = debugInfo;
- }
-
return ret;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerLocal.java b/services/core/java/com/android/server/am/ActivityManagerLocal.java
new file mode 100644
index 0000000..cd4180e
--- /dev/null
+++ b/services/core/java/com/android/server/am/ActivityManagerLocal.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.annotation.NonNull;
+import android.annotation.SystemApi;
+
+/**
+ * Interface for in-process calls into
+ * {@link android.content.Context#ACTIVITY_SERVICE ActivityManager system service}.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public interface ActivityManagerLocal {
+ /**
+ * Checks whether an app will be able to start a foreground service or not.
+ *
+ * @param pid The process id belonging to the app to be checked.
+ * @param uid The UID of the app to be checked.
+ * @param packageName The package name of the app to be checked.
+ * @return whether the app will be able to start a foreground service or not.
+ */
+ boolean canStartForegroundService(int pid, int uid, @NonNull String packageName);
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 06a1abb..83cbf66 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -345,6 +345,7 @@
import com.android.server.DisplayThread;
import com.android.server.IntentResolver;
import com.android.server.IoThread;
+import com.android.server.LocalManagerRegistry;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
import com.android.server.NetworkManagementInternal;
@@ -2327,6 +2328,8 @@
mAppOpsService.publish();
Slog.d("AppOps", "AppOpsService published");
LocalServices.addService(ActivityManagerInternal.class, mInternal);
+ LocalManagerRegistry.addManager(ActivityManagerLocal.class,
+ (ActivityManagerLocal) mInternal);
mActivityTaskManager.onActivityManagerInternalAdded();
mPendingIntentController.onActivityManagerInternalAdded();
mAppProfiler.onActivityManagerInternalAdded();
@@ -15086,7 +15089,8 @@
}
@VisibleForTesting
- public final class LocalService extends ActivityManagerInternal {
+ public final class LocalService extends ActivityManagerInternal
+ implements ActivityManagerLocal {
@Override
public String checkContentProviderAccess(String authority, int userId) {
return mCpHelper.checkContentProviderAccess(authority, userId);
@@ -16008,6 +16012,13 @@
public void unregisterAnrController(AnrController controller) {
mActivityTaskManager.unregisterAnrController(controller);
}
+
+ @Override
+ public boolean canStartForegroundService(int pid, int uid, @NonNull String packageName) {
+ synchronized (ActivityManagerService.this) {
+ return mServices.canStartForegroundServiceLocked(pid, uid, packageName);
+ }
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index ed8d696..4972aa7 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -786,7 +786,7 @@
mAppDataIsolationEnabled =
SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
mVoldAppDataIsolationEnabled = SystemProperties.getBoolean(
- ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
+ ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
mAppDataIsolationAllowlistedApps = new ArrayList<>(
SystemConfig.getInstance().getAppDataIsolationWhitelistedApps());
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 68f10a5..1950710 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -61,6 +61,8 @@
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.database.ContentObserver;
+import android.hardware.SensorPrivacyManager;
+import android.hardware.SensorPrivacyManagerInternal;
import android.hardware.hdmi.HdmiAudioSystemClient;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPlaybackClient;
@@ -520,6 +522,7 @@
/** Interface for UserManagerService. */
private final UserManagerInternal mUserManagerInternal;
private final ActivityManagerInternal mActivityManagerInternal;
+ private final SensorPrivacyManagerInternal mSensorPrivacyManagerInternal;
private final UserRestrictionsListener mUserRestrictionsListener =
new AudioServiceUserRestrictionsListener();
@@ -720,9 +723,12 @@
private String mEnabledSurroundFormats;
private boolean mSurroundModeChanged;
+ private boolean mSupportsMicPrivacyToggle;
+
private boolean mMicMuteFromSwitch;
private boolean mMicMuteFromApi;
private boolean mMicMuteFromRestrictions;
+ private boolean mMicMuteFromPrivacyToggle;
// caches the value returned by AudioSystem.isMicrophoneMuted()
private boolean mMicMuteFromSystemCached;
@@ -822,6 +828,8 @@
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mSensorPrivacyManagerInternal =
+ LocalServices.getService(SensorPrivacyManagerInternal.class);
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mAudioEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleAudioEvent");
@@ -831,6 +839,9 @@
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
mHasVibrator = mVibrator == null ? false : mVibrator.hasVibrator();
+ mSupportsMicPrivacyToggle = mContext.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_MICROPHONE_TOGGLE);
+
// Initialize volume
// Priority 1 - Android Property
// Priority 2 - Audio Policy Service
@@ -1106,6 +1117,16 @@
}
}
+ if (mSupportsMicPrivacyToggle) {
+ mSensorPrivacyManagerInternal.addSensorPrivacyListenerForAllUsers(
+ SensorPrivacyManager.Sensors.MICROPHONE, (userId, enabled) -> {
+ if (userId == getCurrentUserId()) {
+ mMicMuteFromPrivacyToggle = enabled;
+ setMicrophoneMuteNoCallerCheck(getCurrentUserId());
+ }
+ });
+ }
+
mNm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
sendMsg(mAudioHandler,
@@ -3840,11 +3861,12 @@
* @return true if microphone is reported as muted by primary HAL
*/
public boolean isMicrophoneMuted() {
- return mMicMuteFromSystemCached;
+ return mMicMuteFromSystemCached && !mMicMuteFromPrivacyToggle;
}
private boolean isMicrophoneSupposedToBeMuted() {
- return mMicMuteFromSwitch || mMicMuteFromRestrictions || mMicMuteFromApi;
+ return mMicMuteFromSwitch || mMicMuteFromRestrictions || mMicMuteFromApi
+ || mMicMuteFromPrivacyToggle;
}
private void setMicrophoneMuteNoCallerCheck(int userId) {
@@ -7474,6 +7496,13 @@
// the current audio focus owner is no longer valid
mMediaFocusControl.discardAudioFocusOwner();
+ if (mSupportsMicPrivacyToggle) {
+ mMicMuteFromPrivacyToggle = mSensorPrivacyManagerInternal
+ .isSensorPrivacyEnabled(getCurrentUserId(),
+ SensorPrivacyManager.Sensors.MICROPHONE);
+ setMicrophoneMuteNoCallerCheck(getCurrentUserId());
+ }
+
// load volume settings for new user
readAudioSettings(true /*userSwitch*/);
// preserve STREAM_MUSIC volume from one user to the next.
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 1d8f210..07a653f 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
@@ -137,7 +137,7 @@
final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal(
prop.commonProps.sensorId, prop.commonProps.sensorStrength,
prop.commonProps.maxEnrollmentsPerUser, false /* supportsFaceDetection */,
- prop.halControlsPreview);
+ prop.halControlsPreview, false /* resetLockoutRequiresChallenge */);
final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
internalProp);
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 1b9bd7f..afe7f24 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
@@ -332,7 +332,8 @@
@NonNull BiometricScheduler scheduler) {
mSensorProperties = new FaceSensorPropertiesInternal(sensorId,
Utils.authenticatorStrengthToPropertyStrength(strength),
- maxTemplatesAllowed, false /* supportsFaceDetect */, supportsSelfIllumination);
+ maxTemplatesAllowed, false /* supportsFaceDetect */, supportsSelfIllumination,
+ true /* resetLockoutRequiresChallenge */);
mContext = context;
mSensorId = sensorId;
mScheduler = scheduler;
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 6e22a79..cc3b569a 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
@@ -347,7 +347,8 @@
final @FingerprintSensorProperties.SensorType int sensorType =
mIsUdfps ? FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
: FingerprintSensorProperties.TYPE_REAR;
- // resetLockout is controlled by the framework, so hardwareAuthToken is not required
+ // IBiometricsFingerprint@2.1 does not manage timeout below the HAL, so the Gatekeeper HAT
+ // cannot be checked
final boolean resetLockoutRequiresHardwareAuthToken = false;
final int maxEnrollmentsPerUser = mContext.getResources()
.getInteger(R.integer.config_fingerprintMaxTemplatesPerUser);
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 6712c54..5bf15dc 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -22,6 +22,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -43,6 +44,8 @@
import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IUserManager;
import android.os.Parcel;
@@ -55,10 +58,15 @@
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.autofill.AutofillManagerInternal;
+import android.view.textclassifier.TextClassificationContext;
+import android.view.textclassifier.TextClassificationManager;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
import android.widget.Toast;
import com.android.internal.R;
@@ -170,6 +178,8 @@
// DeviceConfig properties
private static final String PROPERTY_SHOW_ACCESS_NOTIFICATIONS = "show_access_notifications";
private static final boolean DEFAULT_SHOW_ACCESS_NOTIFICATIONS = true;
+ private static final String PROPERTY_MAX_CLASSIFICATION_LENGTH = "max_classification_length";
+ private static final int DEFAULT_MAX_CLASSIFICATION_LENGTH = 400;
private final ActivityManagerInternal mAmInternal;
private final IUriGrantsManager mUgm;
@@ -180,8 +190,10 @@
private final AppOpsManager mAppOps;
private final ContentCaptureManagerInternal mContentCaptureInternal;
private final AutofillManagerInternal mAutofillInternal;
+ private final TextClassificationManager mTextClassificationManager;
private final IBinder mPermissionOwner;
private final HostClipboardMonitor mHostClipboardMonitor;
+ private final Handler mWorkerHandler;
@GuardedBy("mLock")
private final SparseArray<PerUserClipboard> mClipboards = new SparseArray<>();
@@ -189,6 +201,9 @@
@GuardedBy("mLock")
private boolean mShowAccessNotifications = DEFAULT_SHOW_ACCESS_NOTIFICATIONS;
+ @GuardedBy("mLock")
+ private int mMaxClassificationLength = DEFAULT_MAX_CLASSIFICATION_LENGTH;
+
private final Object mLock = new Object();
/**
@@ -206,6 +221,8 @@
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
mContentCaptureInternal = LocalServices.getService(ContentCaptureManagerInternal.class);
mAutofillInternal = LocalServices.getService(AutofillManagerInternal.class);
+ mTextClassificationManager = (TextClassificationManager)
+ getContext().getSystemService(Context.TEXT_CLASSIFICATION_SERVICE);
final IBinder permOwner = mUgmInternal.newUriPermissionOwner("clipboard");
mPermissionOwner = permOwner;
if (IS_EMULATOR) {
@@ -232,6 +249,10 @@
updateConfig();
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_CLIPBOARD,
getContext().getMainExecutor(), properties -> updateConfig());
+
+ HandlerThread workerThread = new HandlerThread(TAG);
+ workerThread.start();
+ mWorkerHandler = workerThread.getThreadHandler();
}
@Override
@@ -250,6 +271,8 @@
synchronized (mLock) {
mShowAccessNotifications = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CLIPBOARD,
PROPERTY_SHOW_ACCESS_NOTIFICATIONS, DEFAULT_SHOW_ACCESS_NOTIFICATIONS);
+ mMaxClassificationLength = DeviceConfig.getInt(DeviceConfig.NAMESPACE_CLIPBOARD,
+ PROPERTY_MAX_CLASSIFICATION_LENGTH, DEFAULT_MAX_CLASSIFICATION_LENGTH);
}
}
@@ -592,6 +615,10 @@
}
}
+ if (clip != null) {
+ startClassificationLocked(clip);
+ }
+
// Update this user
final int userId = UserHandle.getUserId(uid);
setPrimaryClipInternalLocked(getClipboardLocked(userId), clip, uid, sourcePackage);
@@ -691,6 +718,68 @@
}
}
+ @GuardedBy("mLock")
+ private void startClassificationLocked(@NonNull ClipData clip) {
+ TextClassifier classifier;
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ classifier = mTextClassificationManager.createTextClassificationSession(
+ new TextClassificationContext.Builder(
+ getContext().getPackageName(),
+ TextClassifier.WIDGET_TYPE_CLIPBOARD
+ ).build()
+ );
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+
+ if (clip.getItemCount() == 0) {
+ clip.getDescription().setClassificationStatus(
+ ClipDescription.CLASSIFICATION_NOT_PERFORMED);
+ return;
+ }
+ CharSequence text = clip.getItemAt(0).getText();
+ if (TextUtils.isEmpty(text) || text.length() > mMaxClassificationLength
+ || text.length() > classifier.getMaxGenerateLinksTextLength()) {
+ clip.getDescription().setClassificationStatus(
+ ClipDescription.CLASSIFICATION_NOT_PERFORMED);
+ return;
+ }
+
+ mWorkerHandler.post(() -> doClassification(text, clip, classifier));
+ }
+
+ @WorkerThread
+ private void doClassification(
+ CharSequence text, ClipData clip, TextClassifier classifier) {
+ TextLinks.Request request = new TextLinks.Request.Builder(text).build();
+ TextLinks links;
+ try {
+ links = classifier.generateLinks(request);
+ } finally {
+ classifier.destroy();
+ }
+
+ // Find the highest confidence for each entity in the text.
+ ArrayMap<String, Float> confidences = new ArrayMap<>();
+ for (TextLinks.TextLink link : links.getLinks()) {
+ for (int i = 0; i < link.getEntityCount(); i++) {
+ String entity = link.getEntity(i);
+ float conf = link.getConfidenceScore(entity);
+ if (conf > confidences.getOrDefault(entity, 0f)) {
+ confidences.put(entity, conf);
+ }
+ }
+ }
+
+ synchronized (mLock) {
+ clip.getDescription().setConfidenceScores(confidences);
+ if (!links.getLinks().isEmpty()) {
+ clip.getItemAt(0).setTextLinks(links);
+ }
+ }
+ }
+
private boolean isDeviceLocked(@UserIdInt int userId) {
final long token = Binder.clearCallingIdentity();
try {
@@ -922,6 +1011,10 @@
if (!mShowAccessNotifications) {
return;
}
+ if (Settings.Secure.getInt(getContext().getContentResolver(),
+ Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS, 1) == 0) {
+ return;
+ }
// Don't notify if the app accessing the clipboard is the same as the current owner.
if (UserHandle.isSameApp(uid, clipboard.primaryClipUid)) {
return;
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index 4f6b530..7a5abf8 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -16,7 +16,6 @@
package com.android.server.connectivity;
-import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_FAILURE;
@@ -33,6 +32,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.net.ConnectivityManager;
import android.net.IDnsResolver;
import android.net.InetAddresses;
import android.net.LinkProperties;
@@ -128,7 +128,7 @@
private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
public static PrivateDnsConfig getPrivateDnsConfig(ContentResolver cr) {
- final String mode = getPrivateDnsMode(cr);
+ final String mode = ConnectivityManager.getPrivateDnsMode(cr);
final boolean useTls = !TextUtils.isEmpty(mode) && !PRIVATE_DNS_MODE_OFF.equals(mode);
@@ -479,13 +479,6 @@
return result;
}
- private static String getPrivateDnsMode(ContentResolver cr) {
- String mode = getStringSetting(cr, PRIVATE_DNS_MODE);
- if (TextUtils.isEmpty(mode)) mode = getStringSetting(cr, PRIVATE_DNS_DEFAULT_MODE);
- if (TextUtils.isEmpty(mode)) mode = PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
- return mode;
- }
-
private static String getStringSetting(ContentResolver cr, String which) {
return Settings.Global.getString(cr, which);
}
diff --git a/services/core/java/com/android/server/connectivity/PacProxyInstaller.java b/services/core/java/com/android/server/connectivity/PacProxyService.java
similarity index 75%
rename from services/core/java/com/android/server/connectivity/PacProxyInstaller.java
rename to services/core/java/com/android/server/connectivity/PacProxyService.java
index aadaf4d..d23b488 100644
--- a/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
+++ b/services/core/java/com/android/server/connectivity/PacProxyService.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.WorkerThread;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -26,12 +28,16 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.net.IPacProxyInstalledListener;
+import android.net.IPacProxyManager;
import android.net.ProxyInfo;
import android.net.TrafficStats;
import android.net.Uri;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -44,6 +50,7 @@
import com.android.net.IProxyCallback;
import com.android.net.IProxyPortListener;
import com.android.net.IProxyService;
+import com.android.net.module.util.PermissionUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -53,7 +60,7 @@
/**
* @hide
*/
-public class PacProxyInstaller {
+public class PacProxyService extends IPacProxyManager.Stub {
private static final String PAC_PACKAGE = "com.android.pacprocessor";
private static final String PAC_SERVICE = "com.android.pacprocessor.PacService";
private static final String PAC_SERVICE_NAME = "com.android.net.IProxyService";
@@ -61,7 +68,7 @@
private static final String PROXY_PACKAGE = "com.android.proxyhandler";
private static final String PROXY_SERVICE = "com.android.proxyhandler.ProxyService";
- private static final String TAG = "PacProxyInstaller";
+ private static final String TAG = "PacProxyService";
private static final String ACTION_PAC_REFRESH = "android.net.proxy.PAC_REFRESH";
@@ -71,10 +78,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,8 +96,8 @@
private volatile boolean mHasSentBroadcast;
private volatile boolean mHasDownloaded;
- private Handler mConnectivityHandler;
- private final int mProxyMessage;
+ private final RemoteCallbackList<IPacProxyInstalledListener>
+ mCallbacks = new RemoteCallbackList<>();
/**
* Used for locking when setting mProxyService and all references to mCurrentPac.
@@ -102,6 +105,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,10 +156,10 @@
}
}
- public PacProxyInstaller(Context context, Handler handler, int proxyMessage) {
+ public PacProxyService(@NonNull Context context) {
mContext = context;
mLastPort = -1;
- final HandlerThread netThread = new HandlerThread("android.pacproxyinstaller",
+ final HandlerThread netThread = new HandlerThread("android.pacproxyservice",
android.os.Process.THREAD_PRIORITY_DEFAULT);
netThread.start();
mNetThreadHandler = new Handler(netThread.getLooper());
@@ -158,8 +168,6 @@
context, 0, new Intent(ACTION_PAC_REFRESH), PendingIntent.FLAG_IMMUTABLE);
context.registerReceiver(new PacRefreshIntentReceiver(),
new IntentFilter(ACTION_PAC_REFRESH));
- mConnectivityHandler = handler;
- mProxyMessage = proxyMessage;
}
private AlarmManager getAlarmManager() {
@@ -169,38 +177,52 @@
return mAlarmManager;
}
+ @Override
+ public void addListener(IPacProxyInstalledListener listener) {
+ PermissionUtils.enforceNetworkStackPermissionOr(mContext,
+ android.Manifest.permission.NETWORK_SETTINGS);
+ mCallbacks.register(listener);
+ }
+
+ @Override
+ public void removeListener(IPacProxyInstalledListener listener) {
+ PermissionUtils.enforceNetworkStackPermissionOr(mContext,
+ android.Manifest.permission.NETWORK_SETTINGS);
+ mCallbacks.unregister(listener);
+ }
+
/**
* Updates the PAC Proxy Installer with current Proxy information. This is called by
- * the ProxyTracker directly before a broadcast takes place to allow
- * the PacProxyInstaller to indicate that the broadcast should not be sent and the
- * PacProxyInstaller will trigger a new broadcast when it is ready.
+ * the ProxyTracker through PacProxyManager before a broadcast takes place to allow
+ * the PacProxyService to indicate that the broadcast should not be sent and the
+ * PacProxyService 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();
+ @Override
+ public void setCurrentProxyScriptUrl(@Nullable ProxyInfo proxy) {
+ PermissionUtils.enforceNetworkStackPermissionOr(mContext,
+ android.Manifest.permission.NETWORK_SETTINGS);
+
+ synchronized (mBroadcastStateLock) {
+ if (proxy != null && !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 +297,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 +370,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;
@@ -365,8 +391,9 @@
}
}
};
- mContext.bindService(intent, mProxyConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
+ mContext.bindService(intent,
+ Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE,
+ new HandlerExecutor(mNetThreadHandler), mProxyConnection);
}
private void unbind() {
@@ -383,16 +410,28 @@
}
private void sendPacBroadcast(ProxyInfo proxy) {
- mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
+ final int length = mCallbacks.beginBroadcast();
+ for (int i = 0; i < length; i++) {
+ final IPacProxyInstalledListener listener = mCallbacks.getBroadcastItem(i);
+ if (listener != null) {
+ try {
+ listener.onPacProxyInstalled(null /* network */, proxy);
+ } catch (RemoteException ignored) { }
+ }
+ }
+ mCallbacks.finishBroadcast();
}
- private synchronized void sendProxyIfNeeded() {
- if (!mHasDownloaded || (mLastPort == -1)) {
- return;
- }
- if (!mHasSentBroadcast) {
- sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort));
- mHasSentBroadcast = true;
+ // This method must be called on mNetThreadHandler.
+ 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..8b9c836 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -27,15 +27,19 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.net.Network;
+import android.net.PacProxyManager;
import android.net.Proxy;
import android.net.ProxyInfo;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
import com.android.net.module.util.ProxyUtils;
@@ -67,7 +71,7 @@
// is not set. Individual networks have their own settings that override this. This member
// is set through setDefaultProxy, which is called when the default network changes proxies
// in its LinkProperties, or when ConnectivityService switches to a new default network, or
- // when PacProxyInstaller resolves the proxy.
+ // when PacProxyService resolves the proxy.
@Nullable
@GuardedBy("mProxyLock")
private volatile ProxyInfo mDefaultProxy = null;
@@ -77,16 +81,31 @@
private final Handler mConnectivityServiceHandler;
- // The object responsible for Proxy Auto Configuration (PAC).
- @NonNull
- private final PacProxyInstaller mPacProxyInstaller;
+ private final PacProxyManager mPacProxyManager;
+
+ private class PacProxyInstalledListener implements PacProxyManager.PacProxyInstalledListener {
+ private final int mEvent;
+
+ PacProxyInstalledListener(int event) {
+ mEvent = event;
+ }
+
+ public void onPacProxyInstalled(@Nullable Network network, @NonNull ProxyInfo proxy) {
+ mConnectivityServiceHandler
+ .sendMessage(mConnectivityServiceHandler
+ .obtainMessage(mEvent, new Pair<>(network, proxy)));
+ }
+ }
public ProxyTracker(@NonNull final Context context,
@NonNull final Handler connectivityServiceInternalHandler, final int pacChangedEvent) {
mContext = context;
mConnectivityServiceHandler = connectivityServiceInternalHandler;
- mPacProxyInstaller = new PacProxyInstaller(
- context, connectivityServiceInternalHandler, pacChangedEvent);
+ mPacProxyManager = context.getSystemService(PacProxyManager.class);
+
+ PacProxyInstalledListener listener = new PacProxyInstalledListener(pacChangedEvent);
+ mPacProxyManager.addPacProxyInstalledListener(
+ new HandlerExecutor(mConnectivityServiceHandler), listener);
}
// Convert empty ProxyInfo's to null as null-checks are used to determine if proxies are present
@@ -182,7 +201,7 @@
if (!TextUtils.isEmpty(pacFileUrl)) {
mConnectivityServiceHandler.post(
- () -> mPacProxyInstaller.setCurrentProxyScriptUrl(proxyProperties));
+ () -> mPacProxyManager.setCurrentProxyScriptUrl(proxyProperties));
}
}
}
@@ -226,9 +245,9 @@
final ProxyInfo defaultProxy = getDefaultProxy();
final ProxyInfo proxyInfo = null != defaultProxy ?
defaultProxy : ProxyInfo.buildDirectProxy("", 0, Collections.emptyList());
+ mPacProxyManager.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 +263,10 @@
}
}
+ private boolean shouldSendBroadcast(ProxyInfo proxy) {
+ return Uri.EMPTY.equals(proxy.getPacFileUrl()) || proxy.getPort() > 0;
+ }
+
/**
* Sets the global proxy in memory. Also writes the values to the global settings of the device.
*
@@ -308,10 +331,10 @@
return;
}
- // This call could be coming from the PacProxyInstaller, containing the port of the
+ // This call could be coming from the PacProxyService, containing the port of the
// local proxy. If this new proxy matches the global proxy then copy this proxy to the
// global (to get the correct local port), and send a broadcast.
- // TODO: Switch PacProxyInstaller to have its own message to send back rather than
+ // TODO: Switch PacProxyService to have its own message to send back rather than
// reusing EVENT_HAS_CHANGED_PROXY and this call to handleApplyDefaultProxy.
if ((mGlobalProxy != null) && (proxyInfo != null)
&& (!Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index a2b9b96..91b96dc 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -46,7 +46,6 @@
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.os.BackgroundThread;
import com.android.server.EventLogTags;
-import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData;
import java.io.PrintWriter;
@@ -220,12 +219,14 @@
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
- HysteresisLevels screenBrightnessThresholds, LogicalDisplay display, Context context) {
+ HysteresisLevels screenBrightnessThresholds, LogicalDisplay display, Context context,
+ HighBrightnessModeController hbmController) {
this(new Injector(), callbacks, looper, sensorManager, lightSensor, mapper,
lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
- ambientBrightnessThresholds, screenBrightnessThresholds, display, context
+ ambientBrightnessThresholds, screenBrightnessThresholds, display, context,
+ hbmController
);
}
@@ -236,7 +237,8 @@
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
- HysteresisLevels screenBrightnessThresholds, LogicalDisplay display, Context context) {
+ HysteresisLevels screenBrightnessThresholds, LogicalDisplay display, Context context,
+ HighBrightnessModeController hbmController) {
mInjector = injector;
mContext = context;
mCallbacks = callbacks;
@@ -273,20 +275,7 @@
mPendingForegroundAppPackageName = null;
mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
-
- final DisplayDeviceConfig ddConfig =
- display.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig();
- HighBrightnessModeData hbmData =
- ddConfig != null ? ddConfig.getHighBrightnessModeData() : null;
-
- final Runnable hbmChangeCallback = () -> {
- updateAutoBrightness(true /*sendUpdate*/, false /*userInitiatedChange*/);
- // TODO: b/175937645 - Callback to DisplayManagerService to indicate a change to the HBM
- // allowance has been made so that the brightness limits can be calculated
- // appropriately.
- };
- mHbmController = new HighBrightnessModeController(mHandler, brightnessMin, brightnessMax,
- hbmData, hbmChangeCallback);
+ mHbmController = hbmController;
}
/**
@@ -327,6 +316,7 @@
public void configure(boolean enable, @Nullable BrightnessConfiguration configuration,
float brightness, boolean userChangedBrightness, float adjustment,
boolean userChangedAutoBrightnessAdjustment, int displayPolicy) {
+ mHbmController.setAutoBrightnessEnabled(enable);
// While dozing, the application processor may be suspended which will prevent us from
// receiving new information from the light sensor. On some devices, we may be able to
// switch to a wake-up light sensor instead but for now we will simply disable the sensor
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 06010f5..251b579 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -110,6 +110,7 @@
private static final String ATTR_TIMESTAMP = "timestamp";
private static final String ATTR_PACKAGE_NAME = "packageName";
private static final String ATTR_USER = "user";
+ private static final String ATTR_UNIQUE_DISPLAY_ID = "uniqueDisplayId";
private static final String ATTR_LUX = "lux";
private static final String ATTR_LUX_TIMESTAMPS = "luxTimestamps";
private static final String ATTR_BATTERY_LEVEL = "batteryLevel";
@@ -217,6 +218,9 @@
}
private void backgroundStart(float initialBrightness) {
+ if (DEBUG) {
+ Slog.d(TAG, "Background start");
+ }
readEvents();
readAmbientBrightnessStats();
@@ -311,7 +315,7 @@
*/
public void notifyBrightnessChanged(float brightness, boolean userInitiated,
float powerBrightnessFactor, boolean isUserSetBrightness,
- boolean isDefaultBrightnessConfig) {
+ boolean isDefaultBrightnessConfig, String uniqueDisplayId) {
if (DEBUG) {
Slog.d(TAG, String.format("notifyBrightnessChanged(brightness=%f, userInitiated=%b)",
brightness, userInitiated));
@@ -319,13 +323,13 @@
Message m = mBgHandler.obtainMessage(MSG_BRIGHTNESS_CHANGED,
userInitiated ? 1 : 0, 0 /*unused*/, new BrightnessChangeValues(brightness,
powerBrightnessFactor, isUserSetBrightness, isDefaultBrightnessConfig,
- mInjector.currentTimeMillis()));
+ mInjector.currentTimeMillis(), uniqueDisplayId));
m.sendToTarget();
}
private void handleBrightnessChanged(float brightness, boolean userInitiated,
float powerBrightnessFactor, boolean isUserSetBrightness,
- boolean isDefaultBrightnessConfig, long timestamp) {
+ boolean isDefaultBrightnessConfig, long timestamp, String uniqueDisplayId) {
BrightnessChangeEvent.Builder builder;
synchronized (mDataCollectionLock) {
@@ -350,6 +354,7 @@
builder.setPowerBrightnessFactor(powerBrightnessFactor);
builder.setUserBrightnessPoint(isUserSetBrightness);
builder.setIsDefaultBrightnessConfig(isDefaultBrightnessConfig);
+ builder.setUniqueDisplayId(uniqueDisplayId);
final int readingCount = mLastSensorReadings.size();
if (readingCount == 0) {
@@ -562,6 +567,7 @@
out.attributeLong(null, ATTR_TIMESTAMP, toWrite[i].timeStamp);
out.attribute(null, ATTR_PACKAGE_NAME, toWrite[i].packageName);
out.attributeInt(null, ATTR_USER, userSerialNo);
+ out.attribute(null, ATTR_UNIQUE_DISPLAY_ID, toWrite[i].uniqueDisplayId);
out.attributeFloat(null, ATTR_BATTERY_LEVEL, toWrite[i].batteryLevel);
out.attributeBoolean(null, ATTR_NIGHT_MODE, toWrite[i].nightMode);
out.attributeInt(null, ATTR_COLOR_TEMPERATURE,
@@ -646,6 +652,8 @@
builder.setPackageName(parser.getAttributeValue(null, ATTR_PACKAGE_NAME));
builder.setUserId(mInjector.getUserId(mUserManager,
parser.getAttributeInt(null, ATTR_USER)));
+ builder.setUniqueDisplayId(
+ parser.getAttributeValue(null, ATTR_UNIQUE_DISPLAY_ID));
builder.setBatteryLevel(parser.getAttributeFloat(null, ATTR_BATTERY_LEVEL));
builder.setNightMode(parser.getAttributeBoolean(null, ATTR_NIGHT_MODE));
builder.setColorTemperature(
@@ -980,7 +988,8 @@
boolean userInitiatedChange = (msg.arg1 == 1);
handleBrightnessChanged(values.brightness, userInitiatedChange,
values.powerBrightnessFactor, values.isUserSetBrightness,
- values.isDefaultBrightnessConfig, values.timestamp);
+ values.isDefaultBrightnessConfig, values.timestamp,
+ values.uniqueDisplayId);
break;
case MSG_START_SENSOR_LISTENER:
startSensorListener();
@@ -1007,20 +1016,22 @@
}
private static class BrightnessChangeValues {
- final float brightness;
- final float powerBrightnessFactor;
- final boolean isUserSetBrightness;
- final boolean isDefaultBrightnessConfig;
- final long timestamp;
+ public final float brightness;
+ public final float powerBrightnessFactor;
+ public final boolean isUserSetBrightness;
+ public final boolean isDefaultBrightnessConfig;
+ public final long timestamp;
+ public final String uniqueDisplayId;
BrightnessChangeValues(float brightness, float powerBrightnessFactor,
boolean isUserSetBrightness, boolean isDefaultBrightnessConfig,
- long timestamp) {
+ long timestamp, String uniqueDisplayId) {
this.brightness = brightness;
this.powerBrightnessFactor = powerBrightnessFactor;
this.isUserSetBrightness = isUserSetBrightness;
this.isDefaultBrightnessConfig = isDefaultBrightnessConfig;
this.timestamp = timestamp;
+ this.uniqueDisplayId = uniqueDisplayId;
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 96a7416..82ca820 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -376,6 +376,8 @@
private final ColorSpace mWideColorSpace;
private SensorManager mSensorManager;
+ private BrightnessTracker mBrightnessTracker;
+
// Whether minimal post processing is allowed by the user.
@GuardedBy("mSyncRoot")
@@ -1162,7 +1164,7 @@
DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
- dpc.onDisplayChanged();
+ dpc.onDisplayChangedLocked();
}
}
@@ -1851,7 +1853,10 @@
for (int i = 0; i < displayPowerControllerCount; i++) {
mDisplayPowerControllers.valueAt(i).dump(pw);
}
-
+ if (mBrightnessTracker != null) {
+ pw.println();
+ mBrightnessTracker.dump(pw);
+ }
pw.println();
mPersistentDataStore.dump(pw);
}
@@ -1937,9 +1942,12 @@
// initPowerManagement has not yet been called.
return;
}
+ if (mBrightnessTracker == null) {
+ mBrightnessTracker = new BrightnessTracker(mContext, null);
+ }
final DisplayPowerController displayPowerController = new DisplayPowerController(
mContext, mDisplayPowerCallbacks, mPowerHandler, mSensorManager,
- mDisplayBlanker, display);
+ mDisplayBlanker, display, mBrightnessTracker);
mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 7b107b85..7110d3e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -174,6 +174,9 @@
// The ID of the LogicalDisplay tied to this DisplayPowerController.
private final int mDisplayId;
+ // The unique ID of the primary display device currently tied to this logical display
+ private String mUniqueDisplayId;
+
// Tracker for brightness changes.
@Nullable
private final BrightnessTracker mBrightnessTracker;
@@ -350,12 +353,15 @@
private final ColorDisplayServiceInternal mCdsi;
private final float[] mNitsRange;
+ private final HighBrightnessModeController mHbmController;
+
// A record of state for skipping brightness ramps.
private int mSkipRampState = RAMP_STATE_SKIP_NONE;
// The first autobrightness value set when entering RAMP_STATE_SKIP_INITIAL.
private float mInitialAutoBrightness;
+
// The controller for the automatic brightness level.
private AutomaticBrightnessController mAutomaticBrightnessController;
@@ -413,16 +419,15 @@
*/
public DisplayPowerController(Context context,
DisplayPowerCallbacks callbacks, Handler handler,
- SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay) {
+ SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
+ BrightnessTracker brightnessTracker) {
mLogicalDisplay = logicalDisplay;
mDisplayId = mLogicalDisplay.getDisplayIdLocked();
mHandler = new DisplayControllerHandler(handler.getLooper());
if (mDisplayId == Display.DEFAULT_DISPLAY) {
- mBrightnessTracker = new BrightnessTracker(context, null);
mBatteryStats = BatteryStatsService.getService();
} else {
- mBrightnessTracker = null;
mBatteryStats = null;
}
@@ -432,6 +437,8 @@
mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class);
mBlanker = blanker;
mContext = context;
+ mBrightnessTracker = brightnessTracker;
+
PowerManager pm = context.getSystemService(PowerManager.class);
@@ -476,6 +483,8 @@
mSkipScreenOnBrightnessRamp = resources.getBoolean(
com.android.internal.R.bool.config_skipScreenOnBrightnessRamp);
+ mHbmController = createHbmController();
+
if (mUseSoftwareAutoBrightnessConfig) {
final float dozeScaleFactor = resources.getFraction(
com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor,
@@ -535,7 +544,7 @@
PowerManager.BRIGHTNESS_MAX, dozeScaleFactor, lightSensorRate,
initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,
- screenBrightnessThresholds, logicalDisplay, context);
+ screenBrightnessThresholds, logicalDisplay, context, mHbmController);
} else {
mUseSoftwareAutoBrightnessConfig = false;
}
@@ -750,8 +759,10 @@
* when displays get swapped on foldable devices. For example, different brightness properties
* of each display need to be properly reflected in AutomaticBrightnessController.
*/
- public void onDisplayChanged() {
+ public void onDisplayChangedLocked() {
// TODO: b/175821789 - Support high brightness on multiple (folding) displays
+
+ mUniqueDisplayId = mLogicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
}
/**
@@ -774,10 +785,6 @@
mAutomaticBrightnessController.stop();
}
- if (mBrightnessTracker != null) {
- mBrightnessTracker.stop();
- }
-
mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
}
}
@@ -1081,6 +1088,8 @@
mBrightnessTracker.setBrightnessConfiguration(mBrightnessConfiguration);
}
+ boolean updateScreenBrightnessSetting = false;
+
// Apply auto-brightness.
boolean slowChange = false;
if (Float.isNaN(brightnessState)) {
@@ -1097,11 +1106,7 @@
if (mAppliedAutoBrightness && !autoBrightnessAdjustmentChanged) {
slowChange = true; // slowly adapt to auto-brightness
}
- // Tell the rest of the system about the new brightness. Note that we do this
- // before applying the low power or dim transformations so that the slider
- // accurately represents the full possible range, even if they range changes what
- // it means in absolute terms.
- putScreenBrightnessSetting(brightnessState);
+ updateScreenBrightnessSetting = true;
mAppliedAutoBrightness = true;
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC);
} else {
@@ -1119,6 +1124,7 @@
mAppliedAutoBrightness = false;
brightnessAdjustmentFlags = 0;
}
+
// Use default brightness when dozing unless overridden.
if ((Float.isNaN(brightnessState))
&& Display.isDozeState(state)) {
@@ -1129,9 +1135,24 @@
// Apply manual brightness.
if (Float.isNaN(brightnessState)) {
brightnessState = clampScreenBrightness(mCurrentScreenBrightnessSetting);
+ if (brightnessState != mCurrentScreenBrightnessSetting) {
+ // The manually chosen screen brightness is outside of the currently allowed
+ // range (i.e., high-brightness-mode), make sure we tell the rest of the system
+ // by updating the setting.
+ updateScreenBrightnessSetting = true;
+ }
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL);
}
+ if (updateScreenBrightnessSetting) {
+ // Tell the rest of the system about the new brightness in case we had to change it
+ // for things like auto-brightness or high-brightness-mode. Note that we do this
+ // before applying the low power or dim transformations so that the slider
+ // accurately represents the full possible range, even if they range changes what
+ // it means in absolute terms.
+ putScreenBrightnessSetting(brightnessState);
+ }
+
// Apply dimming by at least some minimum amount when user activity
// timeout is about to expire.
if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
@@ -1209,9 +1230,10 @@
// animate to. To avoid this, we check the value first.
// If the brightnessState is off (-1.0f) we still want to animate to the minimum
// brightness (0.0f) to accommodate for LED displays, which can appear bright to the
- // user even when the display is all black.
- float animateValue = brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT
- ? PowerManager.BRIGHTNESS_MIN : brightnessState;
+ // user even when the display is all black. We also clamp here in case some
+ // transformations to the brightness have pushed it outside of the currently
+ // allowed range.
+ float animateValue = clampScreenBrightness(brightnessState);
final float currentBrightness = mPowerState.getScreenBrightness();
if (isValidBrightnessValue(animateValue)
&& !BrightnessSynchronizer.floatEquals(animateValue, currentBrightness)) {
@@ -1354,6 +1376,15 @@
msg.sendToTarget();
}
+ private HighBrightnessModeController createHbmController() {
+ final DisplayDeviceConfig ddConfig =
+ mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig();
+ final DisplayDeviceConfig.HighBrightnessModeData hbmData =
+ ddConfig != null ? ddConfig.getHighBrightnessModeData() : null;
+ return new HighBrightnessModeController(mHandler, PowerManager.BRIGHTNESS_MIN,
+ PowerManager.BRIGHTNESS_MAX, hbmData, () -> sendUpdatePowerStateLocked());
+ }
+
private void blockScreenOn() {
if (mPendingScreenOnUnblocker == null) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
@@ -1469,10 +1500,10 @@
private float clampScreenBrightness(float value) {
if (Float.isNaN(value)) {
- return PowerManager.BRIGHTNESS_MIN;
+ value = PowerManager.BRIGHTNESS_MIN;
}
- return MathUtils.constrain(
- value, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
+ return MathUtils.constrain(value,
+ mHbmController.getCurrentBrightnessMin(), mHbmController.getCurrentBrightnessMax());
}
// Checks whether the brightness is within the valid brightness range, not including the off or
@@ -1869,7 +1900,7 @@
: 1.0f;
mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated,
powerFactor, hadUserDataPoint,
- mAutomaticBrightnessController.isDefaultConfig());
+ mAutomaticBrightnessController.isDefaultConfig(), mUniqueDisplayId);
}
}
@@ -2037,11 +2068,6 @@
mAutomaticBrightnessController.dump(pw);
}
- if (mBrightnessTracker != null) {
- pw.println();
- mBrightnessTracker.dump(pw);
- }
-
pw.println();
if (mDisplayWhiteBalanceController != null) {
mDisplayWhiteBalanceController.dump(pw);
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index 12b810f..2e5561d 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -37,7 +37,7 @@
class HighBrightnessModeController {
private static final String TAG = "HighBrightnessModeController";
- private static final boolean DEBUG_HBM = false;
+ private static final boolean DEBUG = false;
private final float mBrightnessMin;
private final float mBrightnessMax;
@@ -48,6 +48,7 @@
private boolean mIsInAllowedAmbientRange = false;
private boolean mIsTimeAvailable = false;
+ private boolean mIsAutoBrightnessEnabled = false;
private float mAutoBrightness;
/**
@@ -84,6 +85,17 @@
};
}
+ void setAutoBrightnessEnabled(boolean isEnabled) {
+ if (isEnabled == mIsAutoBrightnessEnabled) {
+ return;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "setAutoBrightness( " + isEnabled + " )");
+ }
+ mIsAutoBrightnessEnabled = isEnabled;
+ mIsInAllowedAmbientRange = false; // reset when auto-brightness switches
+ }
+
float getCurrentBrightnessMin() {
return mBrightnessMin;
}
@@ -102,7 +114,7 @@
}
void onAmbientLuxChange(float ambientLux) {
- if (!deviceSupportsHbm()) {
+ if (!deviceSupportsHbm() || !mIsAutoBrightnessEnabled) {
return;
}
@@ -132,7 +144,7 @@
mEvents.addFirst(new HbmEvent(mRunningStartTimeMillis, currentTime));
mRunningStartTimeMillis = -1;
- if (DEBUG_HBM) {
+ if (DEBUG) {
Slog.d(TAG, "New HBM event: " + mEvents.getFirst());
}
}
@@ -142,7 +154,7 @@
}
private boolean isCurrentlyAllowed() {
- return mIsTimeAvailable && mIsInAllowedAmbientRange;
+ return mIsAutoBrightnessEnabled && mIsTimeAvailable && mIsInAllowedAmbientRange;
}
private boolean deviceSupportsHbm() {
@@ -167,7 +179,7 @@
timeAlreadyUsed = currentTime - mRunningStartTimeMillis;
}
- if (DEBUG_HBM) {
+ if (DEBUG) {
Slog.d(TAG, "Time already used after current session: " + timeAlreadyUsed);
}
@@ -187,7 +199,7 @@
timeAlreadyUsed += event.endTimeMillis - startTimeMillis;
}
- if (DEBUG_HBM) {
+ if (DEBUG) {
Slog.d(TAG, "Time already used after all sessions: " + timeAlreadyUsed);
}
@@ -220,7 +232,7 @@
nextTimeout = timeWhenMinIsGainedBack;
}
- if (DEBUG_HBM) {
+ if (DEBUG) {
Slog.d(TAG, "HBM recalculated. IsAllowedWithoutRestrictions: "
+ isAllowedWithoutRestrictions
+ ", isOnlyAllowedToStayOn: " + isOnlyAllowedToStayOn
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index aaec89a..2546118 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -853,14 +853,6 @@
// Do not lock when calling these SurfaceControl methods because they are sync
// operations that may block for a while when setting display power mode.
mSurfaceControlProxy.setDesiredDisplayModeSpecs(displayToken, modeSpecs);
-
- final int sfActiveModeId = mSurfaceControlProxy
- .getDynamicDisplayInfo(displayToken).activeDisplayModeId;
- synchronized (getSyncRoot()) {
- if (updateActiveModeLocked(sfActiveModeId)) {
- updateDeviceInfoLocked();
- }
- }
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
index 947ee24..f6828d1 100644
--- a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
@@ -100,18 +100,22 @@
// Wake-up on <Set Stream Path> was not mandatory before CEC 2.0.
// The message is re-sent at the end of the action for devices that don't support 2.0.
sendSetStreamPath();
- int targetPowerStatus = HdmiControlManager.POWER_STATUS_UNKNOWN;
- HdmiDeviceInfo targetDevice = localDevice().mService.getHdmiCecNetwork().getCecDeviceInfo(
- getTargetAddress());
- if (targetDevice != null) {
- targetPowerStatus = targetDevice.getDevicePowerStatus();
- }
- if (!mIsCec20 || targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) {
+ if (!mIsCec20) {
queryDevicePowerStatus();
- } else if (targetPowerStatus == HdmiControlManager.POWER_STATUS_ON) {
- finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
- return true;
+ } else {
+ int targetPowerStatus = HdmiControlManager.POWER_STATUS_UNKNOWN;
+ HdmiDeviceInfo targetDevice = localDevice().mService.getHdmiCecNetwork()
+ .getCecDeviceInfo(getTargetAddress());
+ if (targetDevice != null) {
+ targetPowerStatus = targetDevice.getDevicePowerStatus();
+ }
+ if (targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) {
+ queryDevicePowerStatus();
+ } else if (targetPowerStatus == HdmiControlManager.POWER_STATUS_ON) {
+ finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+ return true;
+ }
}
mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
addTimer(mState, HdmiConfig.TIMEOUT_MS);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index e6e2f96..03a8338 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -431,6 +431,13 @@
private final SelectRequestBuffer mSelectRequestBuffer = new SelectRequestBuffer();
+ @VisibleForTesting HdmiControlService(Context context, List<Integer> deviceTypes) {
+ super(context);
+ mLocalDevices = deviceTypes;
+ mSettingsObserver = new SettingsObserver(mHandler);
+ mHdmiCecConfig = new HdmiCecConfig(context);
+ }
+
public HdmiControlService(Context context) {
super(context);
List<Integer> deviceTypes = HdmiProperties.device_type();
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index 9d2db94..979e7a4 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -89,7 +89,7 @@
mSource = source();
sendCommand(HdmiCecMessageBuilder.buildTextViewOn(getSourceAddress(), mTargetAddress));
- boolean targetOnBefore = getTargetDevicePowerStatus(mSource, mTargetAddress,
+ boolean is20TargetOnBefore = mIsCec20 && getTargetDevicePowerStatus(mSource, mTargetAddress,
HdmiControlManager.POWER_STATUS_UNKNOWN) == HdmiControlManager.POWER_STATUS_ON;
broadcastActiveSource();
// If the device is not an audio system itself, request the connected audio system to
@@ -98,18 +98,23 @@
sendCommand(HdmiCecMessageBuilder.buildSystemAudioModeRequest(getSourceAddress(),
Constants.ADDR_AUDIO_SYSTEM, getSourcePath(), true));
}
- int targetPowerStatus = getTargetDevicePowerStatus(mSource, mTargetAddress,
- HdmiControlManager.POWER_STATUS_UNKNOWN);
- if (!mIsCec20 || targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) {
+
+ if (!mIsCec20) {
queryDevicePowerStatus();
- } else if (targetPowerStatus == HdmiControlManager.POWER_STATUS_ON) {
- if (!targetOnBefore) {
- // Suppress 2nd <Active Source> message if the target device was already on when
- // the 1st one was sent.
- broadcastActiveSource();
+ } else {
+ int targetPowerStatus = getTargetDevicePowerStatus(mSource, mTargetAddress,
+ HdmiControlManager.POWER_STATUS_UNKNOWN);
+ if (targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) {
+ queryDevicePowerStatus();
+ } else if (targetPowerStatus == HdmiControlManager.POWER_STATUS_ON) {
+ if (!is20TargetOnBefore) {
+ // Suppress 2nd <Active Source> message if the target device was already on when
+ // the 1st one was sent.
+ broadcastActiveSource();
+ }
+ finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+ return true;
}
- finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
- return true;
}
mState = STATE_WAITING_FOR_REPORT_POWER_STATUS;
addTimer(mState, HdmiConfig.TIMEOUT_MS);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 6ab4a69..e9c3ec3 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2381,9 +2381,12 @@
showCurrentInputLocked(mCurFocusedWindow, getAppShowFlags(), null,
SoftInputShowHideReason.ATTACH_NEW_INPUT);
}
+ final InputMethodInfo curInputMethodInfo = mMethodMap.get(mCurId);
+ final boolean suppressesSpellChecker =
+ curInputMethodInfo != null && curInputMethodInfo.suppressesSpellChecker();
return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
session.session, (session.channel != null ? session.channel.dup() : null),
- mCurId, mCurSeq, mCurActivityViewToScreenMatrix);
+ mCurId, mCurSeq, mCurActivityViewToScreenMatrix, suppressesSpellChecker);
}
@Nullable
@@ -2425,7 +2428,7 @@
// party code.
return new InputBindResult(
InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
- null, null, mCurMethodId, mCurSeq, null);
+ null, null, mCurMethodId, mCurSeq, null, false);
}
if (!InputMethodUtils.checkIfPackageBelongsToUid(mAppOpsManager, cs.uid,
@@ -2501,7 +2504,7 @@
requestClientSessionLocked(cs);
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
- null, null, mCurId, mCurSeq, null);
+ null, null, mCurId, mCurSeq, null, false);
} else if (SystemClock.uptimeMillis()
< (mLastBindTime+TIME_TO_RECONNECT)) {
// In this case we have connected to the service, but
@@ -2513,7 +2516,7 @@
// to see if we can get back in touch with the service.
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
- null, null, mCurId, mCurSeq, null);
+ null, null, mCurId, mCurSeq, null, false);
} else {
EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime, 0);
@@ -2553,7 +2556,7 @@
}
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
- null, null, mCurId, mCurSeq, null);
+ null, null, mCurId, mCurSeq, null, false);
}
mCurIntent = null;
Slog.w(TAG, "Failure connecting to input method service: " + mCurIntent);
@@ -3509,7 +3512,7 @@
}
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
- null, null, null, -1, null);
+ null, null, null, -1, null, false);
}
mCurFocusedWindow = windowToken;
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 1dd3d41..ef1489b 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1748,7 +1748,7 @@
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
null, null, data.mCurrentInputMethodInfo.getId(),
- clientInfo.mBindingSequence, null);
+ clientInfo.mBindingSequence, null, false);
case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
clientInfo.mBindingSequence++;
@@ -1770,7 +1770,7 @@
clientInfo.mInputMethodSession,
clientInfo.mWriteChannel.dup(),
data.mCurrentInputMethodInfo.getId(),
- clientInfo.mBindingSequence, null);
+ clientInfo.mBindingSequence, null, false);
case InputMethodClientState.UNREGISTERED:
Slog.e(TAG, "The client is already unregistered.");
return InputBindResult.INVALID_CLIENT;
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 dde45c4..c44089b 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -17,13 +17,16 @@
package com.android.server.location.contexthub;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.hardware.SensorPrivacyManager;
+import android.hardware.SensorPrivacyManagerInternal;
import android.hardware.contexthub.V1_0.AsyncEventType;
import android.hardware.contexthub.V1_0.ContextHub;
import android.hardware.contexthub.V1_0.ContextHubMsg;
@@ -59,6 +62,7 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.DumpUtils;
+import com.android.server.LocalServices;
import com.android.server.location.ContextHubServiceProto;
import java.io.FileDescriptor;
@@ -127,6 +131,8 @@
// Lock object for sendWifiSettingUpdate()
private final Object mSendWifiSettingUpdateLock = new Object();
+ private final SensorPrivacyManagerInternal mSensorPrivacyManagerInternal;
+
/**
* Class extending the callback to register with a Context Hub.
*/
@@ -186,6 +192,7 @@
if (mContextHubWrapper == null) {
mTransactionManager = null;
mClientManager = null;
+ mSensorPrivacyManagerInternal = null;
mDefaultClientMap = Collections.emptyMap();
mContextHubIdToInfoMap = Collections.emptyMap();
mSupportedContextHubPerms = Collections.emptyList();
@@ -208,6 +215,8 @@
mClientManager = new ContextHubClientManager(mContext, mContextHubWrapper);
mTransactionManager = new ContextHubTransactionManager(
mContextHubWrapper.getHub(), mClientManager, mNanoAppStateManager);
+ mSensorPrivacyManagerInternal =
+ LocalServices.getService(SensorPrivacyManagerInternal.class);
HashMap<Integer, IContextHubClient> defaultClientMap = new HashMap<>();
for (int contextHubId : mContextHubIdToInfoMap.keySet()) {
@@ -284,18 +293,16 @@
}
if (mContextHubWrapper.supportsMicrophoneDisableSettingNotifications()) {
- sendMicrophoneDisableSettingUpdate();
+ sendMicrophoneDisableSettingUpdateForCurrentUser();
- SensorPrivacyManager.OnSensorPrivacyChangedListener listener =
- new SensorPrivacyManager.OnSensorPrivacyChangedListener() {
- @Override
- public void onSensorPrivacyChanged(boolean enabled) {
- sendMicrophoneDisableSettingUpdate();
- }
- };
- SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext);
- manager.addSensorPrivacyListener(
- SensorPrivacyManager.Sensors.MICROPHONE, listener);
+ mSensorPrivacyManagerInternal.addSensorPrivacyListenerForAllUsers(
+ SensorPrivacyManager.Sensors.MICROPHONE, (userId, enabled) -> {
+ if (userId == getCurrentUserId()) {
+ Log.d(TAG, "User: " + userId + " enabled: " + enabled);
+ sendMicrophoneDisableSettingUpdate(enabled);
+ }
+ });
+
}
}
@@ -1074,19 +1081,48 @@
}
/**
- * Obtains the latest microphone disable setting value and notifies the
- * Context Hub.
+ * Notifies a microphone disable settings change to the Context Hub.
*/
- private void sendMicrophoneDisableSettingUpdate() {
- SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext);
- boolean disabled = manager.isSensorPrivacyEnabled(
- SensorPrivacyManager.Sensors.MICROPHONE);
- Log.d(TAG, "Mic Disabled Setting: " + disabled);
- mContextHubWrapper.onMicrophoneDisableSettingChanged(disabled);
+ private void sendMicrophoneDisableSettingUpdate(boolean enabled) {
+ Log.d(TAG, "Mic Disabled Setting: " + enabled);
+ mContextHubWrapper.onMicrophoneDisableSettingChanged(enabled);
+ }
+
+ /**
+ * Obtains the latest microphone disabled setting for the current user
+ * and notifies the Context Hub.
+ */
+ private void sendMicrophoneDisableSettingUpdateForCurrentUser() {
+ boolean isEnabled = mSensorPrivacyManagerInternal.isSensorPrivacyEnabled(
+ getCurrentUserId(), SensorPrivacyManager.Sensors.MICROPHONE);
+ sendMicrophoneDisableSettingUpdate(isEnabled);
}
private String getCallingPackageName() {
return mContext.getPackageManager().getNameForUid(Binder.getCallingUid());
}
+
+ private int getCurrentUserId() {
+ final long id = Binder.clearCallingIdentity();
+ try {
+ UserInfo currentUser = ActivityManager.getService().getCurrentUser();
+ return currentUser.id;
+ } catch (RemoteException e) {
+ // Activity manager not running, nothing we can do - assume user 0.
+ } finally {
+ Binder.restoreCallingIdentity(id);
+ }
+ return UserHandle.USER_SYSTEM;
+ }
+
+ /**
+ * Send a microphone disable settings update whenever the foreground user changes.
+ * We always send a settings update regardless of the previous state for the same user
+ * since the CHRE framework is expected to handle repeated identical setting update.
+ */
+ public void onUserChanged() {
+ Log.d(TAG, "User changed to id: " + getCurrentUserId());
+ sendMicrophoneDisableSettingUpdateForCurrentUser();
+ }
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 0a074e1..b10d56b 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -1068,6 +1068,12 @@
public boolean sendMediaButton(String packageName, int pid, int uid,
boolean asSystemService, KeyEvent keyEvent, int sequenceId, ResultReceiver cb) {
try {
+ if (KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
+ final String reason = "action=" + KeyEvent.actionToString(keyEvent.getAction())
+ + ";code=" + KeyEvent.keyCodeToString(keyEvent.getKeyCode());
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
+ }
if (asSystemService) {
mCb.onMediaButton(mContext.getPackageName(), Process.myPid(),
Process.SYSTEM_UID, createMediaButtonIntent(keyEvent), sequenceId, cb);
@@ -1085,6 +1091,12 @@
public boolean sendMediaButton(String packageName, int pid, int uid,
boolean asSystemService, KeyEvent keyEvent) {
try {
+ if (KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
+ final String reason = "action=" + KeyEvent.actionToString(keyEvent.getAction())
+ + ";code=" + KeyEvent.keyCodeToString(keyEvent.getKeyCode());
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
+ }
if (asSystemService) {
mCb.onMediaButton(mContext.getPackageName(), Process.myPid(),
Process.SYSTEM_UID, createMediaButtonIntent(keyEvent), 0, null);
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 23d8429..46ece74 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -63,6 +63,7 @@
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
+import android.os.PowerExemptionManager;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteCallbackList;
@@ -82,9 +83,11 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalManagerRegistry;
import com.android.server.SystemService;
import com.android.server.Watchdog;
import com.android.server.Watchdog.Monitor;
+import com.android.server.am.ActivityManagerLocal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -114,6 +117,13 @@
*/
private static final String MEDIA_BUTTON_RECEIVER = "media_button_receiver";
+ /**
+ * Denotes the duration during which an app receiving a media session callback will be
+ * exempted from FGS-from-BG restriction and so will be allowed to start an FGS even if it is
+ * in the background state while it receives a media session callback.
+ */
+ private static final long FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS = 10_000;
+
private final Context mContext;
private final SessionManagerImpl mSessionManagerImpl;
private final MessageHandler mHandler = new MessageHandler();
@@ -136,6 +146,7 @@
private KeyguardManager mKeyguardManager;
private AudioManager mAudioManager;
private boolean mHasFeatureLeanback;
+ private ActivityManagerLocal mActivityManagerLocal;
// The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
// It's always not null after the MediaSessionService is started.
@@ -219,6 +230,8 @@
final IntentFilter filter = new IntentFilter(
NotificationManager.ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED);
mContext.registerReceiver(mNotificationListenerEnabledChangedReceiver, filter);
+
+ mActivityManagerLocal = LocalManagerRegistry.getManager(ActivityManagerLocal.class);
}
@Override
@@ -538,6 +551,26 @@
throw new IllegalArgumentException("packageName is not owned by the calling process");
}
+ void tempAllowlistTargetPkgIfPossible(int targetUid, String targetPackage,
+ int callingPid, int callingUid, String callingPackage, String reason) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ enforcePackageName(callingPackage, callingUid);
+ if (targetUid != callingUid && mActivityManagerLocal.canStartForegroundService(
+ callingPid, callingUid, callingPackage)) {
+ final Context userContext = mContext.createContextAsUser(
+ UserHandle.of(UserHandle.getUserId(targetUid)), /* flags= */ 0);
+ final PowerExemptionManager powerExemptionManager = userContext.getSystemService(
+ PowerExemptionManager.class);
+ powerExemptionManager.addToTemporaryAllowList(targetPackage,
+ FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS,
+ PowerExemptionManager.REASON_MEDIA_SESSION_CALLBACK, reason);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
/**
* Checks a caller's authorization to register an IRemoteControlDisplay.
* Authorization is granted if one of the following is true:
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 0a443f3..7aaab0c 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -51,6 +51,7 @@
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
import android.os.storage.StorageManager;
@@ -62,6 +63,8 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.dex.ArtManagerService;
+import com.android.server.pm.dex.ArtStatsLogUtils;
+import com.android.server.pm.dex.ArtStatsLogUtils.ArtStatsLogger;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.DexoptUtils;
@@ -99,6 +102,8 @@
private final PowerManager.WakeLock mDexoptWakeLock;
private volatile boolean mSystemReady;
+ private final ArtStatsLogger mArtStatsLogger = new ArtStatsLogger();
+
PackageDexOptimizer(Installer installer, Object installLock, Context context,
String wakeLockTag) {
this.mInstaller = installer;
@@ -252,6 +257,28 @@
profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid,
packageStats, options.isDowngrade(), profileName, dexMetadataPath,
options.getCompilationReason());
+
+ // Only report metrics for base apk for now.
+ // TODO: add ISA and APK type to metrics.
+ if (pkg.getBaseApkPath().equals(path)) {
+ Trace.traceBegin(Trace.TRACE_TAG_PACKAGE_MANAGER, "dex2oat-metrics");
+ try {
+ long sessionId = Math.randomLongInternal();
+ ArtStatsLogUtils.writeStatsLog(
+ mArtStatsLogger,
+ sessionId,
+ path,
+ compilerFilter,
+ sharedGid,
+ packageStats.getCompileTime(path),
+ dexMetadataPath,
+ options.getCompilationReason(),
+ newResult);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
// The end result is:
// - FAILED if any path failed,
// - PERFORMED if at least one path needed compilation,
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
new file mode 100644
index 0000000..3b77c39
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.dex;
+
+import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED;
+import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY;
+import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED;
+import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK;
+import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK;
+
+import android.util.jar.StrictJarFile;
+import android.util.Slog;
+
+import com.android.internal.art.ArtStatsLog;
+import com.android.server.pm.PackageManagerService;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+
+/** Utils class to report ART metrics to statsd. */
+public class ArtStatsLogUtils {
+ private static final String TAG = ArtStatsLogUtils.class.getSimpleName();
+ private static final String PROFILE_DEX_METADATA = "primary.prof";
+ private static final String VDEX_DEX_METADATA = "primary.vdex";
+
+
+ private static final int ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY =
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY;
+ private static final int ART_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED =
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED;
+ private static final int ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED =
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED;
+
+ private static final int ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK =
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK;
+ private static final int ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK =
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK;
+
+ private static final Map<Integer, Integer> COMPILATION_REASON_MAP = new HashMap();
+
+ static {
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_UNKNOWN, ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_UNKNOWN);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_FIRST_BOOT, ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_FIRST_BOOT);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_BOOT_AFTER_OTA, ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_BOOT);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_POST_BOOT, ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_POST_BOOT);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL, ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_FAST, ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_FAST);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_BULK, ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_BULK_SECONDARY,
+ ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_BULK_DOWNGRADED,
+ ART_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED,
+ ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_BACKGROUND_DEXOPT, ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_BG_DEXOPT);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_AB_OTA, ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_AB_OTA);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE,
+ ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INACTIVE);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_SHARED,
+ ArtStatsLog.ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_SHARED);
+ }
+
+ private static final Map<String, Integer> COMPILE_FILTER_MAP = new HashMap();
+
+ static {
+ COMPILE_FILTER_MAP.put("error", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_ERROR);
+ COMPILE_FILTER_MAP.put("unknown", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_UNKNOWN);
+ COMPILE_FILTER_MAP.put("assume-verified", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_ASSUMED_VERIFIED);
+ COMPILE_FILTER_MAP.put("extract", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EXTRACT);
+ COMPILE_FILTER_MAP.put("verify", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_VERIFY);
+ COMPILE_FILTER_MAP.put("quicken", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_QUICKEN);
+ COMPILE_FILTER_MAP.put("space-profile", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPACE_PROFILE);
+ COMPILE_FILTER_MAP.put("space", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPACE);
+ COMPILE_FILTER_MAP.put("speed-profile", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPEED_PROFILE);
+ COMPILE_FILTER_MAP.put("speed", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPEED);
+ COMPILE_FILTER_MAP.put("everything-profile", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EVERYTHING_PROFILE);
+ COMPILE_FILTER_MAP.put("everything", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EVERYTHING);
+ COMPILE_FILTER_MAP.put("run-from-apk", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK);
+ COMPILE_FILTER_MAP.put("run-from-apk-fallback",
+ ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK);
+ COMPILE_FILTER_MAP.put("run-from-vdex-fallback",
+ ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK);
+ }
+
+ public static void writeStatsLog(
+ ArtStatsLogger logger,
+ long sessionId,
+ String path,
+ String compilerFilter,
+ int uid,
+ long compileTime,
+ String dexMetadataPath,
+ int compilationReason,
+ int result) {
+ int dexMetadataType = getDexMetadataType(dexMetadataPath);
+ logger.write(
+ sessionId,
+ uid,
+ compilationReason,
+ compilerFilter,
+ ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_RESULT_CODE,
+ result,
+ dexMetadataType);
+ logger.write(
+ sessionId,
+ uid,
+ compilationReason,
+ compilerFilter,
+ ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_DEX_CODE_BYTES,
+ getDexBytes(path),
+ dexMetadataType);
+ logger.write(
+ sessionId,
+ uid,
+ compilationReason,
+ compilerFilter,
+ ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME,
+ compileTime,
+ dexMetadataType);
+ }
+
+ private static long getDexBytes(String apkPath) {
+ StrictJarFile jarFile = null;
+ long dexBytes = 0;
+ try {
+ jarFile = new StrictJarFile(apkPath,
+ /*verify=*/ false,
+ /*signatureSchemeRollbackProtectionsEnforced=*/ false);
+ Iterator<ZipEntry> it = jarFile.iterator();
+ while (it.hasNext()) {
+ ZipEntry entry = it.next();
+ if (entry.getName().matches("classes(\\d)*[.]dex")) {
+ dexBytes += entry.getSize();
+ }
+ }
+ return dexBytes;
+ } catch (IOException ignore) {
+ Slog.e(TAG, "Error when parsing APK " + apkPath);
+ return -1L;
+ } finally {
+ try {
+ if (jarFile != null) {
+ jarFile.close();
+ }
+ } catch (IOException ignore) {
+ }
+ }
+ }
+
+ private static int getDexMetadataType(String dexMetadataPath) {
+ if (dexMetadataPath == null) {
+ return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__NONE_DEX_METADATA;
+ }
+ StrictJarFile jarFile = null;
+ try {
+ jarFile = new StrictJarFile(dexMetadataPath,
+ /*verify=*/ false,
+ /*signatureSchemeRollbackProtectionsEnforced=*/false);
+ boolean hasProfile = findFileName(jarFile, PROFILE_DEX_METADATA);
+ boolean hasVdex = findFileName(jarFile, VDEX_DEX_METADATA);
+ if (hasProfile && hasVdex) {
+ return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__PROFILE_AND_VDEX;
+ } else if (hasProfile) {
+ return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__PROFILE;
+ } else if (hasVdex) {
+ return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__VDEX;
+ } else {
+ return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__UNKNOWN_DEX_METADATA;
+ }
+ } catch (IOException ignore) {
+ Slog.e(TAG, "Error when parsing dex metadata " + dexMetadataPath);
+ return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__ERROR_DEX_METADATA;
+ } finally {
+ try {
+ if (jarFile != null) {
+ jarFile.close();
+ }
+ } catch (IOException ignore) {
+ }
+ }
+ }
+
+ private static boolean findFileName(StrictJarFile jarFile, String filename) throws IOException {
+ Iterator<ZipEntry> it = jarFile.iterator();
+ while (it.hasNext()) {
+ ZipEntry entry = it.next();
+ if (entry.getName().equals(filename)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static class ArtStatsLogger {
+ public void write(
+ long sessionId,
+ int uid,
+ int compilationReason,
+ String compilerFilter,
+ int kind,
+ long value,
+ int dexMetadataType) {
+ ArtStatsLog.write(
+ ArtStatsLog.ART_DATUM_REPORTED,
+ sessionId,
+ uid,
+ COMPILE_FILTER_MAP.getOrDefault(compilerFilter, ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_UNKNOWN),
+ COMPILATION_REASON_MAP.getOrDefault(compilationReason, ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_UNKNOWN),
+ /*timestamp_millis=*/ 0L,
+ ArtStatsLog.ART_DATUM_REPORTED__THREAD_TYPE__ART_THREAD_MAIN,
+ kind,
+ value,
+ dexMetadataType);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index e3ccb75..27bf8a13 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -737,10 +737,12 @@
}
}
if (locationExtraPackageNames != null) {
- // Also grant location permission to location extra packages.
+ // Also grant location and activity recognition permission to location extra packages.
for (String packageName : locationExtraPackageNames) {
grantPermissionsToSystemPackage(pm, packageName, userId,
ALWAYS_LOCATION_PERMISSIONS);
+ grantSystemFixedPermissionsToSystemPackage(pm, packageName, userId,
+ ACTIVITY_RECOGNITION_PERMISSIONS);
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index ffea6a7..1e49071 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2235,9 +2235,9 @@
/** {@inheritDoc} */
@Override
- public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
- CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
- int logo, int windowFlags, Configuration overrideConfig, int displayId) {
+ public StartingSurface addSplashScreen(IBinder appToken, int userId, String packageName,
+ int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
+ int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId) {
if (!SHOW_SPLASH_SCREENS) {
return null;
}
@@ -2264,10 +2264,12 @@
if (theme != context.getThemeResId() || labelRes != 0) {
try {
- context = context.createPackageContext(packageName, CONTEXT_RESTRICTED);
+ context = context.createPackageContextAsUser(packageName, CONTEXT_RESTRICTED,
+ UserHandle.of(userId));
context.setTheme(theme);
} catch (PackageManager.NameNotFoundException e) {
- // Ignore
+ Slog.w(TAG, "Failed creating package context with package name "
+ + packageName + " for user " + userId, e);
}
}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 0735977b..d512edf 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -714,9 +714,9 @@
* @return The starting surface.
*
*/
- public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
- CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
- int logo, int windowFlags, Configuration overrideConfig, int displayId);
+ StartingSurface addSplashScreen(IBinder appToken, int userId, String packageName,
+ int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
+ int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId);
/**
* Set or clear a window which can behave as the keyguard.
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index a95628f..44f14b4 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -258,8 +258,12 @@
}
}
+ /**
+ * @deprecated Notify occlude status change via remote animation.
+ */
+ @Deprecated
public void setOccluded(boolean isOccluded, boolean animate) {
- if (mKeyguardService != null) {
+ if (!WindowManagerService.sEnableRemoteKeyguardAnimation && mKeyguardService != null) {
if (DEBUG) Log.v(TAG, "setOccluded(" + isOccluded + ") animate=" + animate);
mKeyguardService.setOccluded(isOccluded, animate);
}
diff --git a/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java b/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java
index 628c1d6..9f8b27f 100644
--- a/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java
+++ b/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java
@@ -26,7 +26,7 @@
private SystemMemoryUtil() {}
static Metrics getMetrics() {
- int totalIonKb = (int) Debug.getIonHeapsSizeKb();
+ int totalIonKb = (int) Debug.getDmabufHeapTotalExportedKb();
int gpuTotalUsageKb = (int) Debug.getGpuTotalUsageKb();
int gpuDmaBufUsageKb = (int) Debug.getGpuDmaBufUsageKb();
int dmaBufTotalExportedKb = (int) Debug.getDmabufTotalExportedKb();
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index a1d2f8a..3ae67ae 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -3298,7 +3298,11 @@
values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
sessionToken.toString());
- mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
+ try{
+ mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
+ }catch(IllegalArgumentException ex){
+ Slog.w(TAG, "error in insert db for MSG_LOG_WATCH_START", ex);
+ }
args.recycle();
break;
}
@@ -3313,7 +3317,11 @@
values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
sessionToken.toString());
- mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
+ try{
+ mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
+ }catch(IllegalArgumentException ex){
+ Slog.w(TAG, "error in insert db for MSG_LOG_WATCH_END", ex);
+ }
args.recycle();
break;
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index 3893267..b90408f 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -16,6 +16,7 @@
package com.android.server.vibrator;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.vibrator.IVibratorManager;
import android.os.CombinedVibrationEffect;
@@ -35,9 +36,9 @@
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.Arrays;
+import java.util.Iterator;
import java.util.List;
import java.util.PriorityQueue;
@@ -47,11 +48,16 @@
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.
+ * Extra timeout added to the end of each vibration step to ensure it finishes even when
+ * vibrator callbacks are lost.
*/
private static final long CALLBACKS_EXTRA_TIMEOUT = 100;
+ /** Fixed large duration used to note repeating vibrations to {@link IBatteryStats}. */
+ private static final long BATTERY_STATS_REPEATING_VIBRATION_DURATION = 5_000;
+
+ private static final List<Step> EMPTY_STEP_LIST = new ArrayList<>();
+
/** Callbacks for playing a {@link Vibration}. */
interface VibrationCallbacks {
@@ -83,13 +89,10 @@
private final IBatteryStats mBatteryStatsService;
private final Vibration mVibration;
private final VibrationCallbacks mCallbacks;
- private final SparseArray<VibratorController> mVibrators;
+ private final SparseArray<VibratorController> mVibrators = new SparseArray<>();
+ private final StepQueue mStepQueue = new StepQueue();
- @GuardedBy("mLock")
- @Nullable
- private VibrateStep mCurrentVibrateStep;
- @GuardedBy("mLock")
- private boolean mForceStop;
+ private volatile boolean mForceStop;
VibrationThread(Vibration vib, SparseArray<VibratorController> availableVibrators,
PowerManager.WakeLock wakeLock, IBatteryStats batteryStatsService,
@@ -102,7 +105,6 @@
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));
@@ -110,6 +112,15 @@
}
}
+ Vibration getVibration() {
+ return mVibration;
+ }
+
+ @VisibleForTesting
+ SparseArray<VibratorController> getVibrators() {
+ return mVibrators;
+ }
+
@Override
public void binderDied() {
if (DEBUG) {
@@ -136,8 +147,11 @@
/** Cancel current vibration and shuts down the thread gracefully. */
public void cancel() {
+ mForceStop = true;
synchronized (mLock) {
- mForceStop = true;
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration cancelled");
+ }
mLock.notify();
}
}
@@ -148,11 +162,10 @@
if (DEBUG) {
Slog.d(TAG, "Synced vibration complete reported by vibrator manager");
}
- if (mCurrentVibrateStep != null) {
- for (int i = 0; i < mVibrators.size(); i++) {
- mCurrentVibrateStep.vibratorComplete(mVibrators.keyAt(i));
- }
+ for (int i = 0; i < mVibrators.size(); i++) {
+ mStepQueue.consumeOnVibratorComplete(mVibrators.keyAt(i));
}
+ mLock.notify();
}
}
@@ -162,120 +175,73 @@
if (DEBUG) {
Slog.d(TAG, "Vibration complete reported by vibrator " + vibratorId);
}
- if (mCurrentVibrateStep != null) {
- mCurrentVibrateStep.vibratorComplete(vibratorId);
- }
+ mStepQueue.consumeOnVibratorComplete(vibratorId);
+ mLock.notify();
}
}
- Vibration getVibration() {
- return mVibration;
- }
-
- @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;
+ CombinedVibrationEffect.Sequential effect = toSequential(mVibration.getEffect());
+ int stepsPlayed = 0;
+
+ synchronized (mLock) {
+ mStepQueue.offer(new StartVibrateStep(effect));
+ Step topOfQueue;
+
+ while ((topOfQueue = mStepQueue.peek()) != null) {
+ long waitTime = topOfQueue.calculateWaitTime();
+ if (waitTime <= 0) {
+ stepsPlayed += mStepQueue.consume();
} else {
- mCurrentVibrateStep = null;
+ try {
+ mLock.wait(waitTime);
+ } catch (InterruptedException e) { }
+ }
+ if (mForceStop) {
+ mStepQueue.cancel();
+ return Vibration.Status.CANCELLED;
}
}
- 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;
+
+ // Some effects might be ignored because the specified vibrator don't exist or doesn't
+ // support the effect. We only report ignored here if nothing was played besides the
+ // StartVibrateStep (which means every attempt to turn on the vibrator was ignored).
+ return stepsPlayed > effect.getEffects().size()
+ ? Vibration.Status.FINISHED : Vibration.Status.IGNORED_UNSUPPORTED;
} 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)));
+ private void noteVibratorOn(long duration) {
+ try {
+ if (duration <= 0) {
+ return;
}
- final int stepCount = steps.size();
- for (int i = 0; i < stepCount; i++) {
- if (steps.get(i) instanceof VibrateStep) {
- return steps;
- }
+ if (duration == Long.MAX_VALUE) {
+ // Repeating duration has started. Report a fixed duration here, noteVibratorOff
+ // should be called when this is cancelled.
+ duration = BATTERY_STATS_REPEATING_VIBRATION_DURATION;
}
- // No valid vibrate step was generated, ignore effect completely.
- return Lists.newArrayList();
+ 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) {
}
- 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;
+ 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) {
}
- 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;
}
/**
@@ -306,341 +272,240 @@
return timing;
}
- /**
- * Sleeps until given {@code wakeUpTime}.
- *
- * <p>This stops immediately when {@link #cancel()} is called.
- *
- * @return true if waited until wake-up time, false if it was cancelled.
- */
- private boolean waitUntil(long wakeUpTime) {
- synchronized (mLock) {
- long durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
- while (durationRemaining > 0) {
- try {
- mLock.wait(durationRemaining);
- } catch (InterruptedException e) {
- }
- if (mForceStop) {
- return false;
- }
- durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
- }
+ private static CombinedVibrationEffect.Sequential toSequential(CombinedVibrationEffect effect) {
+ if (effect instanceof CombinedVibrationEffect.Sequential) {
+ return (CombinedVibrationEffect.Sequential) effect;
}
- return true;
+ return (CombinedVibrationEffect.Sequential) CombinedVibrationEffect.startSequential()
+ .addNext(effect)
+ .combine();
}
- /**
- * Sleeps until given {@link VibrateStep#isVibrationComplete()}, or until {@code wakeUpTime}.
- *
- * <p>This stops immediately when {@link #cancel()} is called.
- *
- * @return true if finished on vibration complete, false if it was cancelled or timed out.
- */
- private boolean waitForVibrationComplete(VibrateStep step, long wakeUpTime) {
- synchronized (mLock) {
- long durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
- while (!step.isVibrationComplete() && durationRemaining > 0) {
- try {
- mLock.wait(durationRemaining);
- } catch (InterruptedException e) {
- }
- if (mForceStop) {
- return false;
- }
- durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
- }
- }
- return step.isVibrationComplete();
- }
-
- 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);
-
- /** Returns true if the vibration played by this step is complete. */
- boolean isVibrationComplete();
- }
-
- /** Represent a vibration on a single vibrator. */
- private final class SingleVibrateStep implements VibrateStep {
- private final VibratorController mVibrator;
- private final VibrationEffect mEffect;
+ /** Queue for {@link Step Steps}, sorted by their start time. */
+ private final class StepQueue {
+ @GuardedBy("mLock")
+ private final PriorityQueue<Step> mNextSteps = new PriorityQueue<>();
@GuardedBy("mLock")
- private boolean mVibrationComplete;
-
- SingleVibrateStep(VibratorController vibrator, VibrationEffect effect) {
- mVibrator = vibrator;
- mEffect = effect;
+ public void offer(@NonNull Step step) {
+ mNextSteps.offer(step);
}
@GuardedBy("mLock")
- @Override
- public boolean isVibrationComplete() {
- return mVibrationComplete;
+ @Nullable
+ public Step peek() {
+ return mNextSteps.peek();
+ }
+
+ /**
+ * Play and remove the step at the top of this queue, and also adds the next steps
+ * generated to be played next.
+ *
+ * @return the number of steps played
+ */
+ @GuardedBy("mLock")
+ public int consume() {
+ Step nextStep = mNextSteps.poll();
+ if (nextStep != null) {
+ mNextSteps.addAll(nextStep.play());
+ return 1;
+ }
+ return 0;
+ }
+
+ /**
+ * Play and remove the step in this queue that should be anticipated by the vibrator
+ * completion callback.
+ *
+ * <p>This assumes only one of the next steps is waiting on this given vibrator, so the
+ * first step found is played by this method, in no particular order.
+ */
+ @GuardedBy("mLock")
+ public void consumeOnVibratorComplete(int vibratorId) {
+ Iterator<Step> it = mNextSteps.iterator();
+ List<Step> nextSteps = EMPTY_STEP_LIST;
+ while (it.hasNext()) {
+ Step step = it.next();
+ if (step.shouldPlayWhenVibratorComplete(vibratorId)) {
+ it.remove();
+ nextSteps = step.play();
+ break;
+ }
+ }
+ mNextSteps.addAll(nextSteps);
+ }
+
+ /**
+ * Cancel the current queue, clearing all remaining steps.
+ *
+ * <p>This will remove and trigger {@link Step#cancel()} in all steps, in order.
+ */
+ @GuardedBy("mLock")
+ public void cancel() {
+ Step step;
+ while ((step = mNextSteps.poll()) != null) {
+ step.cancel();
+ }
+ }
+ }
+
+ /**
+ * Represent a single step for playing a vibration.
+ *
+ * <p>Every step has a start time, which can be used to apply delays between steps while
+ * executing them in sequence.
+ */
+ private abstract class Step implements Comparable<Step> {
+ public final long startTime;
+
+ Step(long startTime) {
+ this.startTime = startTime;
+ }
+
+ /** Play this step, returning a (possibly empty) list of next steps. */
+ @NonNull
+ public abstract List<Step> play();
+
+ /** Cancel this pending step. */
+ public void cancel() {
+ }
+
+ /**
+ * Return true to play this step right after a vibrator has notified vibration completed,
+ * used to anticipate steps waiting on vibrator callbacks with a timeout.
+ */
+ public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
+ return false;
+ }
+
+ /** Returns the time in millis to wait before playing this step. */
+ public long calculateWaitTime() {
+ if (startTime == Long.MAX_VALUE) {
+ // This step don't have a predefined start time, it's just marked to be executed
+ // after all other steps have finished.
+ return 0;
+ }
+ return Math.max(0, startTime - SystemClock.uptimeMillis());
}
@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();
- synchronized (mLock) {
- mVibrationComplete = true;
- mLock.notify();
- }
+ public int compareTo(Step o) {
+ return Long.compare(startTime, o.startTime);
+ }
+ }
+
+ /**
+ * Starts a sync vibration.
+ *
+ * <p>If this step has successfully started playing a vibration on any vibrator, it will always
+ * add a {@link FinishVibrateStep} to the queue, to be played after all vibrators have finished
+ * all their individual steps.
+ *
+ * <o>If this step does not start any vibrator, it will add a {@link StartVibrateStep} if the
+ * sequential effect isn't finished yet.
+ */
+ private final class StartVibrateStep extends Step {
+ public final CombinedVibrationEffect.Sequential sequentialEffect;
+ public final int currentIndex;
+
+ StartVibrateStep(CombinedVibrationEffect.Sequential effect) {
+ this(SystemClock.uptimeMillis() + effect.getDelays().get(0), effect, /* index= */ 0);
+ }
+
+ StartVibrateStep(long startTime, CombinedVibrationEffect.Sequential effect, int index) {
+ super(startTime);
+ sequentialEffect = effect;
+ currentIndex = index;
}
@Override
- public Vibration.Status play() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "SingleVibrateStep");
+ public List<Step> play() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "StartVibrateStep");
+ List<Step> nextSteps = new ArrayList<>();
long duration = -1;
try {
if (DEBUG) {
- Slog.d(TAG, "SingleVibrateStep starting...");
+ Slog.d(TAG, "StartVibrateStep for effect #" + currentIndex);
}
- 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.
- if (waitForVibrationComplete(this,
- startTime + duration + CALLBACKS_EXTRA_TIMEOUT)) {
- return Vibration.Status.FINISHED;
- }
- // Timed out or vibration cancelled. Stop vibrator anyway.
- mVibrator.off();
- return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED;
+ CombinedVibrationEffect effect = sequentialEffect.getEffects().get(currentIndex);
+ DeviceEffectMap effectMapping = createEffectToVibratorMapping(effect);
+ if (effectMapping == null) {
+ // Unable to map effects to vibrators, ignore this step.
+ return nextSteps;
}
- 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) {
- if (!waitUntil(amplitudeStep.startTime)) {
- mVibrator.off();
- return Vibration.Status.CANCELLED;
- }
- amplitudeStep.play();
- amplitudeStep = amplitudeStep.nextStep();
- }
-
- return Vibration.Status.FINISHED;
+ duration = startVibrating(effectMapping, nextSteps);
+ noteVibratorOn(duration);
} finally {
- if (duration > 0 && duration < Long.MAX_VALUE) {
- noteVibratorOff();
- }
- if (DEBUG) {
- Slog.d(TAG, "SingleVibrateStep done.");
+ // If this step triggered any vibrator then add a finish step to wait for all
+ // active vibrators to finish their individual steps before going to the next.
+ Step nextStep = duration > 0 ? new FinishVibrateStep(this) : nextStep();
+ if (nextStep != null) {
+ nextSteps.add(nextStep);
}
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
+ return nextSteps;
}
/**
- * 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.
+ * Create the next {@link StartVibrateStep} to play this sequential effect, starting at the
+ * time this method is called, or null if sequence is complete.
*/
- 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);
+ @Nullable
+ private Step nextStep() {
+ int nextIndex = currentIndex + 1;
+ if (nextIndex >= sequentialEffect.getEffects().size()) {
+ return null;
}
- // OneShot and Waveform effects require amplitude change after calling vibrator.on.
- return -1;
+ long nextEffectDelay = sequentialEffect.getDelays().get(nextIndex);
+ long nextStartTime = SystemClock.uptimeMillis() + nextEffectDelay;
+ return new StartVibrateStep(nextStartTime, sequentialEffect, nextIndex);
}
- /**
- * 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);
- }
+ /** Create a mapping of individual {@link VibrationEffect} to available vibrators. */
+ @Nullable
+ private DeviceEffectMap createEffectToVibratorMapping(
+ CombinedVibrationEffect effect) {
+ if (effect instanceof CombinedVibrationEffect.Mono) {
+ return new DeviceEffectMap((CombinedVibrationEffect.Mono) effect);
+ }
+ if (effect instanceof CombinedVibrationEffect.Stereo) {
+ return new DeviceEffectMap((CombinedVibrationEffect.Stereo) effect);
}
return null;
}
- }
-
- /** Represent a synchronized vibration step on multiple vibrators. */
- private final class SyncedVibrateStep implements VibrateStep {
- private final SparseArray<VibrationEffect> mEffects;
- private final long mRequiredCapabilities;
- private final int[] mVibratorIds;
-
- @GuardedBy("mLock")
- private int mActiveVibratorCounter;
-
- SyncedVibrateStep(SparseArray<VibrationEffect> effects) {
- mEffects = effects;
- mActiveVibratorCounter = mEffects.size();
- mRequiredCapabilities = calculateRequiredSyncCapabilities(effects);
- mVibratorIds = new int[effects.size()];
- for (int i = 0; i < effects.size(); i++) {
- mVibratorIds[i] = effects.keyAt(i);
- }
- }
-
- @GuardedBy("mLock")
- @Override
- public boolean isVibrationComplete() {
- return mActiveVibratorCounter <= 0;
- }
-
- @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();
- synchronized (mLock) {
- --mActiveVibratorCounter;
- mLock.notify();
- }
- }
-
- @Override
- public Vibration.Status play() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "SyncedVibrateStep");
- long duration = -1;
- try {
- if (DEBUG) {
- Slog.d(TAG, "SyncedVibrateStep starting...");
- }
- final PriorityQueue<AmplitudeStep> nextSteps = new PriorityQueue<>(mEffects.size());
- long startTime = SystemClock.uptimeMillis();
- duration = startVibratingSynced(startTime, nextSteps);
-
- if (duration <= 0) {
- // Vibrate step failed, vibrator could not be turned on for this step.
- return Vibration.Status.IGNORED;
- }
-
- noteVibratorOn(duration);
- while (!nextSteps.isEmpty()) {
- AmplitudeStep step = nextSteps.poll();
- if (!waitUntil(step.startTime)) {
- stopAllVibrators();
- return Vibration.Status.CANCELLED;
- }
- step.play();
- AmplitudeStep nextStep = step.nextStep();
- if (nextStep == null) {
- // This vibrator has finished playing the effect for this step.
- synchronized (mLock) {
- mActiveVibratorCounter--;
- }
- } else {
- nextSteps.add(nextStep);
- }
- }
-
- synchronized (mLock) {
- // All OneShot and Waveform effects have finished. Just wait for the other
- // effects to end via native callbacks before finishing this synced step.
- final long wakeUpTime = startTime + duration + CALLBACKS_EXTRA_TIMEOUT;
- if (mActiveVibratorCounter <= 0 || waitForVibrationComplete(this, wakeUpTime)) {
- return Vibration.Status.FINISHED;
- }
-
- // Timed out or vibration cancelled. Stop all vibrators anyway.
- stopAllVibrators();
- return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED;
- }
- } finally {
- if (duration > 0) {
- noteVibratorOff();
- }
- if (DEBUG) {
- Slog.d(TAG, "SyncedVibrateStep done.");
- }
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
/**
* Starts playing effects on designated vibrators, in sync.
*
- * @return A positive duration, in millis, to wait for the completion of this effect.
- * Non-positive values indicate the vibrator has ignored this effect. Repeating waveform
- * returns the duration of a single run to be used as timeout for callbacks.
+ * @param effectMapping The {@link CombinedVibrationEffect} mapped to this device vibrators
+ * @param nextSteps An output list to accumulate the future {@link Step Steps} created
+ * by this method, typically one for each vibrator that has
+ * successfully started vibrating on this step.
+ * @return The duration, in millis, of the {@link CombinedVibrationEffect}. Repeating
+ * waveforms return {@link Long#MAX_VALUE}. Zero or negative values indicate the vibrators
+ * have ignored all effects.
*/
- private long startVibratingSynced(long startTime, PriorityQueue<AmplitudeStep> nextSteps) {
+ private long startVibrating(DeviceEffectMap effectMapping, List<Step> nextSteps) {
+ int vibratorCount = effectMapping.size();
+ if (vibratorCount == 0) {
+ // No effect was mapped to any available vibrator.
+ return 0;
+ }
+
+ VibratorOnStep[] steps = new VibratorOnStep[vibratorCount];
+ long vibrationStartTime = SystemClock.uptimeMillis();
+ for (int i = 0; i < vibratorCount; i++) {
+ steps[i] = new VibratorOnStep(vibrationStartTime,
+ mVibrators.get(effectMapping.vibratorIdAt(i)), effectMapping.effectAt(i));
+ }
+
+ if (steps.length == 1) {
+ // No need to prepare and trigger sync effects on a single vibrator.
+ return startVibrating(steps[0], nextSteps);
+ }
+
// This synchronization of vibrators should be executed one at a time, even if we are
// vibrating different sets of vibrators in parallel. The manager can only prepareSynced
// one set of vibrators at a time.
@@ -648,17 +513,24 @@
boolean hasPrepared = false;
boolean hasTriggered = false;
try {
- hasPrepared = mCallbacks.prepareSyncedVibration(mRequiredCapabilities,
- mVibratorIds);
- long timeout = startVibrating(startTime, nextSteps);
+ hasPrepared = mCallbacks.prepareSyncedVibration(
+ effectMapping.getRequiredSyncCapabilities(),
+ effectMapping.getVibratorIds());
- // Check if preparation was successful, otherwise devices area already vibrating
- if (hasPrepared) {
+ long duration = 0;
+ for (VibratorOnStep step : steps) {
+ duration = Math.max(duration, startVibrating(step, nextSteps));
+ }
+
+ // Check if sync was prepared and if any step was accepted by a vibrator,
+ // otherwise there is nothing to trigger here.
+ if (hasPrepared && duration > 0) {
hasTriggered = mCallbacks.triggerSyncedVibration(mVibration.id);
}
- return timeout;
+ return duration;
} finally {
if (hasPrepared && !hasTriggered) {
+ // Trigger has failed or all steps were ignored by the vibrators.
mCallbacks.cancelSyncedVibration();
return 0;
}
@@ -666,77 +538,365 @@
}
}
- /**
- * 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 positive duration, in millis, to wait for the completion of this effect.
- * Non-positive values indicate the vibrator has ignored this effect. Repeating waveform
- * returns the duration of a single run to be used as timeout for callbacks.
- */
- 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));
+ private long startVibrating(VibratorOnStep step, List<Step> nextSteps) {
+ nextSteps.addAll(step.play());
+ return step.getDuration();
+ }
+ }
+
+ /**
+ * Finish a sync vibration started by a {@link StartVibrateStep}.
+ *
+ * <p>This only plays after all active vibrators steps have finished, and adds a {@link
+ * StartVibrateStep} to the queue if the sequential effect isn't finished yet.
+ */
+ private final class FinishVibrateStep extends Step {
+ public final StartVibrateStep startedStep;
+
+ FinishVibrateStep(StartVibrateStep startedStep) {
+ super(Long.MAX_VALUE); // No predefined startTime, just wait for all steps in the queue.
+ this.startedStep = startedStep;
+ }
+
+ @Override
+ public List<Step> play() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "FinishVibrateStep");
+ try {
+ if (DEBUG) {
+ Slog.d(TAG, "FinishVibrateStep for effect #" + startedStep.currentIndex);
+ }
+ noteVibratorOff();
+ Step nextStep = startedStep.nextStep();
+ return nextStep == null ? EMPTY_STEP_LIST : Arrays.asList(nextStep);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
- return maxDuration;
+ }
+
+ @Override
+ public void cancel() {
+ noteVibratorOff();
+ }
+ }
+
+ /**
+ * Represent a step turn the vibrator on.
+ *
+ * <p>No other calls to the vibrator is made from this step, so this can be played in between
+ * calls to 'prepare' and 'trigger' for synchronized vibrations.
+ */
+ private final class VibratorOnStep extends Step {
+ public final VibratorController controller;
+ public final VibrationEffect effect;
+ private long mDuration;
+
+ VibratorOnStep(long startTime, VibratorController controller, VibrationEffect effect) {
+ super(startTime);
+ this.controller = controller;
+ this.effect = effect;
}
/**
- * Play a single effect on a single vibrator.
- *
- * @return A positive duration, in millis, to wait for the completion of this effect.
- * Non-positive values indicate the vibrator has ignored this effect. Repeating waveform
- * returns the duration of a single run to be used as timeout for callbacks.
+ * Return the duration, in millis, of this effect. Repeating waveforms return {@link
+ * Long#MAX_VALUE}. Zero or negative values indicate the vibrator has ignored this effect.
*/
- private long startVibrating(VibratorController controller, VibrationEffect effect,
- long startTime, PriorityQueue<AmplitudeStep> nextSteps) {
- int vibratorId = controller.getVibratorInfo().getId();
- long duration;
+ public long getDuration() {
+ return mDuration;
+ }
+
+ @Override
+ public List<Step> play() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorOnStep");
+ try {
+ if (DEBUG) {
+ Slog.d(TAG, "Turning on vibrator " + controller.getVibratorInfo().getId());
+ }
+ List<Step> nextSteps = new ArrayList<>();
+ mDuration = startVibrating(effect, nextSteps);
+ return nextSteps;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+
+ private long startVibrating(VibrationEffect effect, List<Step> nextSteps) {
+ final long duration;
+ final long now = SystemClock.uptimeMillis();
if (effect instanceof VibrationEffect.OneShot) {
VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect;
duration = oneShot.getDuration();
+ // Do NOT set amplitude here. This might be called between prepareSynced and
+ // triggerSynced, so the vibrator is not actually turned on here.
+ // The next steps will handle the amplitude after the vibrator has turned on.
controller.on(duration, mVibration.id);
- nextSteps.add(
- new AmplitudeStep(vibratorId, oneShot, startTime, startTime + duration));
+ nextSteps.add(new VibratorAmplitudeStep(now, controller, oneShot,
+ now + duration + CALLBACKS_EXTRA_TIMEOUT));
} 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);
+ // Return the full duration of this waveform effect.
+ duration = waveform.getDuration();
+ long onDuration = getVibratorOnDuration(waveform, 0);
+ if (onDuration > 0) {
+ // Do NOT set amplitude here. This might be called between prepareSynced and
+ // triggerSynced, so the vibrator is not actually turned on here.
+ // The next steps will handle the amplitudes after the vibrator has turned on.
+ controller.on(onDuration, mVibration.id);
}
- nextSteps.add(
- new AmplitudeStep(vibratorId, waveform, startTime, startTime + duration));
+ long offTime = onDuration > 0 ? now + onDuration + CALLBACKS_EXTRA_TIMEOUT : now;
+ nextSteps.add(new VibratorAmplitudeStep(now, controller, waveform, offTime));
} 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);
+ if (duration > 0) {
+ nextSteps.add(new VibratorOffStep(now + duration + CALLBACKS_EXTRA_TIMEOUT,
+ controller));
+ } else if (prebaked.getFallbackEffect() != null) {
+ return startVibrating(prebaked.getFallbackEffect(), nextSteps);
}
} else if (effect instanceof VibrationEffect.Composed) {
VibrationEffect.Composed composed = (VibrationEffect.Composed) effect;
duration = controller.on(composed, mVibration.id);
+ if (duration > 0) {
+ nextSteps.add(new VibratorOffStep(now + duration + CALLBACKS_EXTRA_TIMEOUT,
+ controller));
+ }
} else {
duration = 0;
}
return duration;
}
+ }
- private void stopAllVibrators() {
- for (int vibratorId : mVibratorIds) {
- VibratorController controller = mVibrators.get(vibratorId);
- if (controller != null) {
- controller.off();
+ /**
+ * Represents a step to turn the vibrator off.
+ *
+ * <p>This runs after a timeout on the expected time the vibrator should have finished playing,
+ * and can anticipated by vibrator complete callbacks.
+ */
+ private final class VibratorOffStep extends Step {
+ public final VibratorController controller;
+
+ VibratorOffStep(long startTime, VibratorController controller) {
+ super(startTime);
+ this.controller = controller;
+ }
+
+ @Override
+ public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
+ return controller.getVibratorInfo().getId() == vibratorId;
+ }
+
+ @Override
+ public List<Step> play() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorOffStep");
+ try {
+ stopVibrating();
+ return EMPTY_STEP_LIST;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+
+ @Override
+ public void cancel() {
+ stopVibrating();
+ }
+
+ private void stopVibrating() {
+ if (DEBUG) {
+ Slog.d(TAG, "Turning off vibrator " + controller.getVibratorInfo().getId());
+ }
+ controller.off();
+ }
+ }
+
+ /** Represents a step to change the amplitude of the vibrator. */
+ private final class VibratorAmplitudeStep extends Step {
+ public final VibratorController controller;
+ public final VibrationEffect.Waveform waveform;
+ public final int currentIndex;
+ public final long expectedVibratorStopTime;
+
+ private long mNextVibratorStopTime;
+
+ VibratorAmplitudeStep(long startTime, VibratorController controller,
+ VibrationEffect.OneShot oneShot, long expectedVibratorStopTime) {
+ this(startTime, controller,
+ (VibrationEffect.Waveform) VibrationEffect.createWaveform(
+ new long[]{oneShot.getDuration()}, new int[]{oneShot.getAmplitude()},
+ /* repeat= */ -1),
+ expectedVibratorStopTime);
+ }
+
+ VibratorAmplitudeStep(long startTime, VibratorController controller,
+ VibrationEffect.Waveform waveform, long expectedVibratorStopTime) {
+ this(startTime, controller, waveform, /* index= */ 0, expectedVibratorStopTime);
+ }
+
+ VibratorAmplitudeStep(long startTime, VibratorController controller,
+ VibrationEffect.Waveform waveform, int index, long expectedVibratorStopTime) {
+ super(startTime);
+ this.controller = controller;
+ this.waveform = waveform;
+ this.currentIndex = index;
+ this.expectedVibratorStopTime = expectedVibratorStopTime;
+ mNextVibratorStopTime = expectedVibratorStopTime;
+ }
+
+ @Override
+ public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
+ if (controller.getVibratorInfo().getId() == vibratorId) {
+ mNextVibratorStopTime = SystemClock.uptimeMillis();
+ }
+ return false;
+ }
+
+ @Override
+ public List<Step> play() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorAmplitudeStep");
+ try {
+ if (DEBUG) {
+ long latency = SystemClock.uptimeMillis() - startTime;
+ Slog.d(TAG, "Running amplitude step with " + latency + "ms latency.");
+ }
+ if (waveform.getTimings()[currentIndex] == 0) {
+ // Skip waveform entries with zero timing.
+ return nextSteps();
+ }
+ int amplitude = waveform.getAmplitudes()[currentIndex];
+ if (amplitude == 0) {
+ stopVibrating();
+ return nextSteps();
+ }
+ if (startTime >= mNextVibratorStopTime) {
+ // 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) {
+ startVibrating(onDuration);
+ mNextVibratorStopTime =
+ SystemClock.uptimeMillis() + onDuration + CALLBACKS_EXTRA_TIMEOUT;
+ }
+ }
+ changeAmplitude(amplitude);
+ return nextSteps();
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+
+ @Override
+ public void cancel() {
+ stopVibrating();
+ }
+
+ private void stopVibrating() {
+ if (DEBUG) {
+ Slog.d(TAG, "Turning off vibrator " + controller.getVibratorInfo().getId());
+ }
+ controller.off();
+ mNextVibratorStopTime = SystemClock.uptimeMillis();
+ }
+
+ private void startVibrating(long duration) {
+ if (DEBUG) {
+ Slog.d(TAG, "Turning on vibrator " + controller.getVibratorInfo().getId() + " for "
+ + duration + "ms");
+ }
+ controller.on(duration, mVibration.id);
+ }
+
+ private void changeAmplitude(int amplitude) {
+ if (DEBUG) {
+ Slog.d(TAG, "Amplitude changed on vibrator " + controller.getVibratorInfo().getId()
+ + " to " + amplitude);
+ }
+ controller.setAmplitude(amplitude);
+ }
+
+ @NonNull
+ private List<Step> nextSteps() {
+ long nextStartTime = startTime + waveform.getTimings()[currentIndex];
+ int nextIndex = currentIndex + 1;
+ if (nextIndex >= waveform.getTimings().length) {
+ nextIndex = waveform.getRepeatIndex();
+ }
+ if (nextIndex < 0) {
+ return Arrays.asList(new VibratorOffStep(nextStartTime, controller));
+ }
+ return Arrays.asList(new VibratorAmplitudeStep(nextStartTime, controller, waveform,
+ nextIndex, mNextVibratorStopTime));
+ }
+ }
+
+ /**
+ * Map a {@link CombinedVibrationEffect} to the vibrators available on the device.
+ *
+ * <p>This contains the logic to find the capabilities required from {@link IVibratorManager} to
+ * play all of the effects in sync.
+ */
+ private final class DeviceEffectMap {
+ private final SparseArray<VibrationEffect> mVibratorEffects;
+ private final int[] mVibratorIds;
+ private final long mRequiredSyncCapabilities;
+
+ DeviceEffectMap(CombinedVibrationEffect.Mono mono) {
+ mVibratorEffects = new SparseArray<>(mVibrators.size());
+ mVibratorIds = new int[mVibrators.size()];
+ for (int i = 0; i < mVibrators.size(); i++) {
+ int vibratorId = mVibrators.keyAt(i);
+ mVibratorEffects.put(vibratorId, mono.getEffect());
+ mVibratorIds[i] = vibratorId;
+ }
+ mRequiredSyncCapabilities = calculateRequiredSyncCapabilities(mVibratorEffects);
+ }
+
+ DeviceEffectMap(CombinedVibrationEffect.Stereo stereo) {
+ SparseArray<VibrationEffect> stereoEffects = stereo.getEffects();
+ mVibratorEffects = new SparseArray<>();
+ for (int i = 0; i < stereoEffects.size(); i++) {
+ int vibratorId = stereoEffects.keyAt(i);
+ if (mVibrators.contains(vibratorId)) {
+ mVibratorEffects.put(vibratorId, stereoEffects.valueAt(i));
}
}
+ mVibratorIds = new int[mVibratorEffects.size()];
+ for (int i = 0; i < mVibratorEffects.size(); i++) {
+ mVibratorIds[i] = mVibratorEffects.keyAt(i);
+ }
+ mRequiredSyncCapabilities = calculateRequiredSyncCapabilities(mVibratorEffects);
+ }
+
+ /**
+ * Return the number of vibrators mapped to play the {@link CombinedVibrationEffect} on this
+ * device.
+ */
+ public int size() {
+ return mVibratorIds.length;
+ }
+
+ /**
+ * Return all capabilities required to play the {@link CombinedVibrationEffect} in
+ * between calls to {@link IVibratorManager#prepareSynced} and
+ * {@link IVibratorManager#triggerSynced}.
+ */
+ public long getRequiredSyncCapabilities() {
+ return mRequiredSyncCapabilities;
+ }
+
+ /** Return all vibrator ids mapped to play the {@link CombinedVibrationEffect}. */
+ public int[] getVibratorIds() {
+ return mVibratorIds;
+ }
+
+ /** Return the id of the vibrator at given index. */
+ public int vibratorIdAt(int index) {
+ return mVibratorEffects.keyAt(index);
+ }
+
+ /** Return the {@link VibrationEffect} at given index. */
+ public VibrationEffect effectAt(int index) {
+ return mVibratorEffects.valueAt(index);
}
/**
@@ -782,145 +942,4 @@
&& (prepareCapabilities & ~capability) != 0;
}
}
-
- /** 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 nextStartTime = startTime + waveform.getTimings()[currentIndex];
- int nextIndex = currentIndex + 1;
- if (nextIndex >= waveform.getTimings().length) {
- nextIndex = waveform.getRepeatIndex();
- }
- return new AmplitudeStep(vibratorId, waveform, nextIndex, nextStartTime,
- 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...");
- }
- if (waitUntil(SystemClock.uptimeMillis() + mDelay)) {
- return Vibration.Status.FINISHED;
- }
- return Vibration.Status.CANCELLED;
- } finally {
- if (DEBUG) {
- Slog.d(TAG, "DelayStep done.");
- }
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index db3d7ad..89dac05 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -664,13 +664,16 @@
// naturally.
private boolean mInSizeCompatModeForBounds = false;
- // Whether this activity is letterboxed for fixed orientation. If letterboxed due to fixed
- // orientation then aspect ratio restrictions are also already respected.
+ // Bounds populated in resolveFixedOrientationConfiguration when this activity is letterboxed
+ // for fixed orientation. If not null, they are used as parent container in
+ // resolveSizeCompatModeConfiguration and in a constructor of CompatDisplayInsets. If
+ // letterboxed due to fixed orientation then aspect ratio restrictions are also respected.
// This happens when an activity has fixed orientation which doesn't match orientation of the
// parent because a display is ignoring orientation request or fixed to user rotation.
// See WindowManagerService#getIgnoreOrientationRequest and
// WindowManagerService#getFixedToUserRotation for more context.
- private boolean mIsLetterboxedForFixedOrientationAndAspectRatio = false;
+ @Nullable
+ private Rect mLetterboxBoundsForFixedOrientationAndAspectRatio;
// activity is not displayed?
// TODO: rename to mNoDisplay
@@ -6863,7 +6866,7 @@
}
// TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
- private void updateCompatDisplayInsets(@Nullable Rect fixedOrientationBounds) {
+ private void updateCompatDisplayInsets() {
if (mCompatDisplayInsets != null || !shouldCreateCompatDisplayInsets()) {
// The override configuration is set only once in size compatibility mode.
return;
@@ -6891,7 +6894,8 @@
// The role of CompatDisplayInsets is like the override bounds.
mCompatDisplayInsets =
- new CompatDisplayInsets(mDisplayContent, this, fixedOrientationBounds);
+ new CompatDisplayInsets(
+ mDisplayContent, this, mLetterboxBoundsForFixedOrientationAndAspectRatio);
}
@VisibleForTesting
@@ -6945,8 +6949,6 @@
|| windowingMode == WINDOWING_MODE_FULLSCREEN) {
resolveFixedOrientationConfiguration(newParentConfiguration);
}
- final Rect fixedOrientationBounds = isLetterboxedForFixedOrientationAndAspectRatio()
- ? new Rect(resolvedConfig.windowConfiguration.getBounds()) : null;
if (mCompatDisplayInsets != null) {
resolveSizeCompatModeConfiguration(newParentConfiguration);
@@ -6966,7 +6968,7 @@
}
if (mVisibleRequested) {
- updateCompatDisplayInsets(fixedOrientationBounds);
+ updateCompatDisplayInsets();
}
// TODO(b/175212232): Consolidate position logic from each "resolve" method above here.
@@ -7001,7 +7003,7 @@
* WindowManagerService#getIgnoreOrientationRequest} for more context.
*/
boolean isLetterboxedForFixedOrientationAndAspectRatio() {
- return mIsLetterboxedForFixedOrientationAndAspectRatio;
+ return mLetterboxBoundsForFixedOrientationAndAspectRatio != null;
}
/**
@@ -7012,7 +7014,7 @@
* in this methiod.
*/
private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig) {
- mIsLetterboxedForFixedOrientationAndAspectRatio = false;
+ mLetterboxBoundsForFixedOrientationAndAspectRatio = null;
if (handlesOrientationChangeFromDescendant()) {
// No need to letterbox because of fixed orientation. Display will handle
// fixed-orientation requests.
@@ -7089,7 +7091,7 @@
// Calculate app bounds using fixed orientation bounds because they will be needed later
// for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}.
task.computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
- mIsLetterboxedForFixedOrientationAndAspectRatio = true;
+ mLetterboxBoundsForFixedOrientationAndAspectRatio = new Rect(resolvedBounds);
}
/**
@@ -7635,6 +7637,12 @@
if (getConfiguration().equals(mTmpConfig) && !forceNewConfig && !displayChanged) {
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration & display "
+ "unchanged in %s", this);
+ // It's possible that resolveOverrideConfiguration was called before mVisibleRequested
+ // became true and mCompatDisplayInsets may not have been created so ensure
+ // that mCompatDisplayInsets is created here.
+ if (mVisibleRequested) {
+ updateCompatDisplayInsets();
+ }
return true;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index d4eedf1..52d110c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -557,7 +557,7 @@
boolean mSupportsPictureInPicture;
boolean mSupportsMultiDisplay;
boolean mForceResizableActivities;
- boolean mSupportsNonResizableMultiWindow;
+ volatile boolean mSupportsNonResizableMultiWindow;
final List<ActivityTaskManagerInternal.ScreenObserver> mScreenObservers = new ArrayList<>();
@@ -3432,6 +3432,11 @@
}
@Override
+ public boolean supportsNonResizableMultiWindow() {
+ return mSupportsNonResizableMultiWindow;
+ }
+
+ @Override
public boolean updateConfiguration(Configuration values) {
mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 426e631..9f60878 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1862,6 +1862,7 @@
// Update application display metrics.
final WmDisplayCutout wmDisplayCutout = calculateDisplayCutoutForRotation(rotation);
final DisplayCutout displayCutout = wmDisplayCutout.getDisplayCutout();
+ final RoundedCorners roundedCorners = calculateRoundedCornersForRotation(rotation);
final int appWidth = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
displayCutout);
@@ -1878,6 +1879,7 @@
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
}
mDisplayInfo.displayCutout = displayCutout.isEmpty() ? null : displayCutout;
+ mDisplayInfo.roundedCorners = roundedCorners;
mDisplayInfo.getAppMetrics(mDisplayMetrics);
if (mDisplayScalingDisabled) {
mDisplayInfo.flags |= Display.FLAG_SCALING_DISABLED;
diff --git a/services/core/java/com/android/server/wm/DisplayHashController.java b/services/core/java/com/android/server/wm/DisplayHashController.java
index 7e16c22..1262dee 100644
--- a/services/core/java/com/android/server/wm/DisplayHashController.java
+++ b/services/core/java/com/android/server/wm/DisplayHashController.java
@@ -17,7 +17,9 @@
package com.android.server.wm;
import static android.service.displayhash.DisplayHasherService.EXTRA_VERIFIED_DISPLAY_HASH;
-import static android.service.displayhash.DisplayHasherService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
+import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM;
+import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN;
+import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -32,7 +34,6 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -45,16 +46,21 @@
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.service.displayhash.DisplayHashParams;
import android.service.displayhash.DisplayHasherService;
import android.service.displayhash.IDisplayHasherService;
+import android.util.Size;
import android.util.Slog;
import android.view.MagnificationSpec;
+import android.view.SurfaceControl;
import android.view.displayhash.DisplayHash;
import android.view.displayhash.VerifiedDisplayHash;
import com.android.internal.annotations.GuardedBy;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -78,12 +84,15 @@
private final Context mContext;
/**
- * Lock used for the cached {@link #mHashAlgorithms} array
+ * Lock used for the cached {@link #mDisplayHashAlgorithms} map
*/
- private final Object mHashAlgorithmsLock = new Object();
+ private final Object mDisplayHashAlgorithmsLock = new Object();
- @GuardedBy("mHashingAlgorithmsLock")
- private String[] mHashAlgorithms;
+ /**
+ * The cached map of display hash algorithms to the {@link DisplayHashParams}
+ */
+ @GuardedBy("mDisplayHashAlgorithmsLock")
+ private Map<String, DisplayHashParams> mDisplayHashAlgorithms;
private final Handler mHandler;
@@ -104,34 +113,8 @@
}
String[] getSupportedHashAlgorithms() {
- // We have a separate lock for the hashing algorithm array since it doesn't need to make
- // the request through the service connection. Instead, we have a lock to ensure we can
- // properly cache the hashing algorithms array so we don't need to call into the
- // ExtServices process for each request.
- synchronized (mHashAlgorithmsLock) {
- // Already have cached values
- if (mHashAlgorithms != null) {
- return mHashAlgorithms;
- }
-
- final ServiceInfo serviceInfo = getServiceInfo();
- if (serviceInfo == null) return null;
-
- final PackageManager pm = mContext.getPackageManager();
- final Resources res;
- try {
- res = pm.getResourcesForApplication(serviceInfo.applicationInfo);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.e(TAG, "Error getting application resources for " + serviceInfo, e);
- return null;
- }
-
- final int resourceId = serviceInfo.metaData.getInt(
- SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS);
- mHashAlgorithms = res.getStringArray(resourceId);
-
- return mHashAlgorithms;
- }
+ Map<String, DisplayHashParams> displayHashAlgorithms = getDisplayHashAlgorithms();
+ return displayHashAlgorithms.keySet().toArray(new String[0]);
}
@Nullable
@@ -148,13 +131,76 @@
return results.getParcelable(EXTRA_VERIFIED_DISPLAY_HASH);
}
- void generateDisplayHash(HardwareBuffer buffer, Rect bounds,
+ private void generateDisplayHash(HardwareBuffer buffer, Rect bounds,
String hashAlgorithm, RemoteCallback callback) {
connectAndRun(
service -> service.generateDisplayHash(mSalt, buffer, bounds, hashAlgorithm,
callback));
}
+ void generateDisplayHash(SurfaceControl.LayerCaptureArgs.Builder args,
+ Rect boundsInWindow, String hashAlgorithm, RemoteCallback callback) {
+ final Map<String, DisplayHashParams> displayHashAlgorithmsMap = getDisplayHashAlgorithms();
+ DisplayHashParams displayHashParams = displayHashAlgorithmsMap.get(hashAlgorithm);
+ if (displayHashParams == null) {
+ Slog.w(TAG, "Failed to generateDisplayHash. Invalid hashAlgorithm");
+ sendDisplayHashError(callback, DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM);
+ return;
+ }
+
+ Size size = displayHashParams.getBufferSize();
+ if (size != null && (size.getWidth() > 0 || size.getHeight() > 0)) {
+ args.setFrameScale((float) size.getWidth() / boundsInWindow.width(),
+ (float) size.getHeight() / boundsInWindow.height());
+ }
+
+ args.setGrayscale(displayHashParams.isGrayscaleBuffer());
+
+ SurfaceControl.ScreenshotHardwareBuffer screenshotHardwareBuffer =
+ SurfaceControl.captureLayers(args.build());
+ if (screenshotHardwareBuffer == null
+ || screenshotHardwareBuffer.getHardwareBuffer() == null) {
+ Slog.w(TAG, "Failed to generate DisplayHash. Couldn't capture content");
+ sendDisplayHashError(callback, DISPLAY_HASH_ERROR_UNKNOWN);
+ return;
+ }
+
+ generateDisplayHash(screenshotHardwareBuffer.getHardwareBuffer(), boundsInWindow,
+ hashAlgorithm, callback);
+ }
+
+ private Map<String, DisplayHashParams> getDisplayHashAlgorithms() {
+ // We have a separate lock for the hashing params to ensure we can properly cache the
+ // hashing params so we don't need to call into the ExtServices process for each request.
+ synchronized (mDisplayHashAlgorithmsLock) {
+ if (mDisplayHashAlgorithms != null) {
+ return mDisplayHashAlgorithms;
+ }
+
+ final SyncCommand syncCommand = new SyncCommand();
+ Bundle results = syncCommand.run((service, remoteCallback) -> {
+ try {
+ service.getDisplayHashAlgorithms(remoteCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to invoke getDisplayHashAlgorithms command", e);
+ }
+ });
+
+ mDisplayHashAlgorithms = new HashMap<>(results.size());
+ for (String key : results.keySet()) {
+ mDisplayHashAlgorithms.put(key, results.getParcelable(key));
+ }
+
+ return mDisplayHashAlgorithms;
+ }
+ }
+
+ void sendDisplayHashError(RemoteCallback callback, int errorCode) {
+ Bundle bundle = new Bundle();
+ bundle.putInt(EXTRA_DISPLAY_HASH_ERROR_CODE, errorCode);
+ callback.sendResult(bundle);
+ }
+
/**
* Calculate the bounds to generate the hash for. This takes into account window transform,
* magnification, and display bounds.
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 01f0359..d929d50 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -448,7 +448,8 @@
final Looper looper = UiThread.getHandler().getLooper();
mHandler = new PolicyHandler(looper);
- mSystemGestures = new SystemGesturesPointerEventListener(mContext, mHandler,
+ // TODO(b/181821798) Migrate SystemGesturesPointerEventListener to use window context.
+ mSystemGestures = new SystemGesturesPointerEventListener(mUiContext, mHandler,
new SystemGesturesPointerEventListener.Callbacks() {
@Override
public void onSwipeFromTop() {
diff --git a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
index 999c585..62e4a85 100644
--- a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
+++ b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
@@ -16,12 +16,15 @@
package com.android.server.wm;
+import static java.lang.Integer.toHexString;
+
import android.app.UriGrantsManager;
import android.content.ClipData;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.Log;
import com.android.internal.view.IDragAndDropPermissions;
import com.android.server.LocalServices;
@@ -32,6 +35,9 @@
class DragAndDropPermissionsHandler extends IDragAndDropPermissions.Stub
implements IBinder.DeathRecipient {
+ private static final String TAG = "DragAndDrop";
+ private static final boolean DEBUG = false;
+
private final WindowManagerGlobalLock mGlobalLock;
private final int mSourceUid;
private final String mTargetPackage;
@@ -43,7 +49,7 @@
private IBinder mActivityToken = null;
private IBinder mPermissionOwnerToken = null;
- private IBinder mTransientToken = null;
+ private IBinder mAppToken = null;
DragAndDropPermissionsHandler(WindowManagerGlobalLock lock, ClipData clipData, int sourceUid,
String targetPackage, int mode, int sourceUserId, int targetUserId) {
@@ -62,6 +68,10 @@
if (mActivityToken != null || mPermissionOwnerToken != null) {
return;
}
+ if (DEBUG) {
+ Log.d(TAG, this + ": taking permissions bound to activity: "
+ + toHexString(activityToken.hashCode()));
+ }
mActivityToken = activityToken;
// Will throw if Activity is not found.
@@ -84,14 +94,18 @@
}
@Override
- public void takeTransient(IBinder transientToken) throws RemoteException {
+ public void takeTransient(IBinder appToken) throws RemoteException {
if (mActivityToken != null || mPermissionOwnerToken != null) {
return;
}
+ if (DEBUG) {
+ Log.d(TAG, this + ": taking permissions bound to app process: "
+ + toHexString(appToken.hashCode()));
+ }
mPermissionOwnerToken = LocalServices.getService(UriGrantsManagerInternal.class)
.newUriPermissionOwner("drop");
- mTransientToken = transientToken;
- mTransientToken.linkToDeath(this, 0);
+ mAppToken = appToken;
+ mAppToken.linkToDeath(this, 0);
doTake(mPermissionOwnerToken);
}
@@ -112,11 +126,17 @@
} finally {
mActivityToken = null;
}
+ if (DEBUG) {
+ Log.d(TAG, this + ": releasing activity-bound permissions");
+ }
} else {
permissionOwner = mPermissionOwnerToken;
mPermissionOwnerToken = null;
- mTransientToken.unlinkToDeath(this, 0);
- mTransientToken = null;
+ mAppToken.unlinkToDeath(this, 0);
+ mAppToken = null;
+ if (DEBUG) {
+ Log.d(TAG, this + ": releasing process-bound permissions");
+ }
}
UriGrantsManagerInternal ugm = LocalServices.getService(UriGrantsManagerInternal.class);
@@ -139,6 +159,9 @@
@Override
public void binderDied() {
+ if (DEBUG) {
+ Log.d(TAG, this + ": app process died: " + toHexString(mAppToken.hashCode()));
+ }
try {
release();
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 35e5491..45c4233 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -30,6 +30,7 @@
import static com.android.server.wm.InsetsSourceProviderProto.CONTROL_TARGET;
import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL;
import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL_TARGET;
+import static com.android.server.wm.InsetsSourceProviderProto.FINISH_SEAMLESS_ROTATE_FRAME_NUMBER;
import static com.android.server.wm.InsetsSourceProviderProto.FRAME;
import static com.android.server.wm.InsetsSourceProviderProto.IME_OVERRIDDEN_FRAME;
import static com.android.server.wm.InsetsSourceProviderProto.IS_LEASH_READY_FOR_DISPATCHING;
@@ -58,7 +59,6 @@
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import java.io.PrintWriter;
-import java.util.function.Consumer;
/**
* Controller for a specific inset source on the server. It's called provider as it provides the
@@ -84,16 +84,6 @@
private final Rect mImeOverrideFrame = new Rect();
private boolean mIsLeashReadyForDispatching;
- private final Consumer<Transaction> mSetLeashPositionConsumer = t -> {
- if (mControl != null) {
- final SurfaceControl leash = mControl.getLeash();
- if (leash != null) {
- final Point position = mControl.getSurfacePosition();
- t.setPosition(leash, position.x, position.y);
- }
- }
- };
-
/** The visibility override from the current controlling window. */
private boolean mClientVisible;
@@ -159,6 +149,7 @@
// TODO: Ideally, we should wait for the animation to finish so previous window can
// 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);
@@ -257,16 +248,31 @@
if (mControl != null) {
final Point position = getWindowFrameSurfacePosition();
if (mControl.setSurfacePosition(position.x, position.y) && mControlTarget != null) {
- if (mWin.getWindowFrames().didFrameSizeChange()) {
- mWin.applyWithNextDraw(mSetLeashPositionConsumer);
+ if (!mWin.getWindowFrames().didFrameSizeChange()) {
+ updateLeashPosition(-1 /* frameNumber */);
+ } else if (mWin.mInRelayout) {
+ updateLeashPosition(mWin.getFrameNumber());
} else {
- mSetLeashPositionConsumer.accept(mWin.getPendingTransaction());
+ mWin.mPendingPositionChanged = this;
}
mStateController.notifyControlChanged(mControlTarget);
}
}
}
+ void updateLeashPosition(long frameNumber) {
+ if (mControl == null) {
+ return;
+ }
+ final SurfaceControl leash = mControl.getLeash();
+ if (leash != null) {
+ final Transaction t = mDisplayContent.getPendingTransaction();
+ final Point position = mControl.getSurfacePosition();
+ t.setPosition(leash, position.x, position.y);
+ deferTransactionUntil(t, leash, frameNumber);
+ }
+ }
+
private Point getWindowFrameSurfacePosition() {
final Rect frame = mWin.getFrame();
final Point position = new Point();
@@ -274,6 +280,14 @@
return position;
}
+ private void deferTransactionUntil(Transaction t, SurfaceControl leash, long frameNumber) {
+ if (frameNumber >= 0) {
+ final SurfaceControl barrier = mWin.getClientViewRootSurface();
+ t.deferTransactionUntil(mWin.getSurfaceControl(), barrier, frameNumber);
+ t.deferTransactionUntil(leash, barrier, frameNumber);
+ }
+ }
+
/**
* @see InsetsStateController#onControlFakeTargetChanged(int, InsetsControlTarget)
*/
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 914e456..64ff108 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -20,6 +20,10 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.graphics.Matrix.MSCALE_X;
+import static android.graphics.Matrix.MSCALE_Y;
+import static android.graphics.Matrix.MSKEW_X;
+import static android.graphics.Matrix.MSKEW_Y;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
@@ -229,7 +233,8 @@
}
@Override
- public void setFinishTaskBounds(int taskId, Rect destinationBounds) {
+ public void setFinishTaskBounds(int taskId, Rect destinationBounds, Rect windowCrop,
+ float[] float9) {
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
"setFinishTaskBounds(%d): bounds=%s", taskId, destinationBounds);
final long token = Binder.clearCallingIdentity();
@@ -239,6 +244,8 @@
final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
if (taskAdapter.mTask.mTaskId == taskId) {
taskAdapter.mFinishBounds.set(destinationBounds);
+ taskAdapter.mFinishWindowCrop.set(windowCrop);
+ taskAdapter.mFinishTransform = float9;
break;
}
}
@@ -1084,6 +1091,9 @@
private final Rect mLocalBounds = new Rect();
// The bounds of the target when animation is finished
private final Rect mFinishBounds = new Rect();
+ // Bounds and transform for the final transaction.
+ private final Rect mFinishWindowCrop = new Rect();
+ private float[] mFinishTransform;
TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) {
mTask = task;
@@ -1120,13 +1130,31 @@
void onCleanup() {
if (!mFinishBounds.isEmpty()) {
- // Apply any pending bounds changes
- final SurfaceControl taskSurface = mTask.getSurfaceControl();
- mTask.getPendingTransaction()
- .setPosition(taskSurface, mFinishBounds.left, mFinishBounds.top)
- .setWindowCrop(taskSurface, mFinishBounds.width(), mFinishBounds.height())
+ final SurfaceControl taskSurface = mTask.mSurfaceControl;
+ final Transaction pendingTransaction = mTask.getPendingTransaction();
+ if (mFinishTransform != null) {
+ pendingTransaction
+ .setMatrix(taskSurface,
+ mFinishTransform[MSCALE_X], mFinishTransform[MSKEW_Y],
+ mFinishTransform[MSKEW_X], mFinishTransform[MSCALE_Y]);
+ }
+ float left = mFinishBounds.left;
+ float top = mFinishBounds.top;
+ if (!mFinishWindowCrop.isEmpty()) {
+ pendingTransaction.setWindowCrop(taskSurface, mFinishWindowCrop);
+ if (mFinishTransform != null) {
+ // adjust the position for insets.
+ left -= mFinishWindowCrop.left * mFinishTransform[MSCALE_X];
+ top -= mFinishWindowCrop.top * mFinishTransform[MSCALE_Y];
+ }
+ }
+ pendingTransaction
+ .setPosition(taskSurface, left, top)
.apply();
mTask.mLastRecentsAnimationBounds.set(mFinishBounds);
+ // reset the variables
+ mFinishTransform = null;
+ mFinishWindowCrop.setEmpty();
mFinishBounds.setEmpty();
} else if (!mTask.isAttached()) {
// Apply the task's pending transaction in case it is detached and its transaction
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 1212302..2a768e7 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2818,7 +2818,7 @@
* @param launchParams The resolved launch params to use.
* @param realCallingPid The pid from {@link ActivityStarter#setRealCallingPid}
* @param realCallingUid The uid from {@link ActivityStarter#setRealCallingUid}
- * @return The roott task to use for the launch or INVALID_TASK_ID.
+ * @return The root task to use for the launch or INVALID_TASK_ID.
*/
Task getLaunchRootTask(@Nullable ActivityRecord r,
@Nullable ActivityOptions options, @Nullable Task candidateTask, boolean onTop,
@@ -2887,7 +2887,7 @@
// Falling back to default task container
taskDisplayArea = taskDisplayArea.mDisplayContent.getDefaultTaskDisplayArea();
rootTask = taskDisplayArea.getOrCreateRootTask(r, options, candidateTask,
- activityType, onTop);
+ launchParams, activityType, onTop);
if (rootTask != null) {
return rootTask;
}
@@ -2942,7 +2942,8 @@
}
}
- return container.getOrCreateRootTask(r, options, candidateTask, activityType, onTop);
+ return container.getOrCreateRootTask(
+ r, options, candidateTask, launchParams, activityType, onTop);
}
/** @return true if activity record is null or can be launched on provided display. */
diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
index 1e8b8a5..4cc369f 100644
--- a/services/core/java/com/android/server/wm/SeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -102,6 +102,10 @@
* window in the new orientation.
*/
void finish(Transaction t, WindowContainer win) {
+ if (win.mSurfaceControl == null || !win.mSurfaceControl.isValid()) {
+ return;
+ }
+
mTransform.reset();
t.setMatrix(win.mSurfaceControl, mTransform, mFloat9);
t.setPosition(win.mSurfaceControl, win.mLastSurfacePosition.x, win.mLastSurfacePosition.y);
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index 6c46135..0708569 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -52,8 +52,8 @@
int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId) {
if (!DEBUG_ENABLE_SHELL_DRAWER) {
- return mService.mPolicy.addSplashScreen(activity.token, packageName, theme,
- compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
+ return mService.mPolicy.addSplashScreen(activity.token, activity.mUserId, packageName,
+ theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
overrideConfig, displayId);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a4b4726..ad0ce5b 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4911,9 +4911,15 @@
task.mLastNonFullscreenBounds = lastNonFullscreenBounds;
task.setBounds(lastNonFullscreenBounds);
task.mWindowLayoutAffinity = windowLayoutAffinity;
+ if (activities.size() > 0) {
+ // We need to add the task into hierarchy before adding child to it.
+ final DisplayContent dc =
+ taskSupervisor.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY);
+ dc.getDefaultTaskDisplayArea().addChild(task, POSITION_BOTTOM);
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- task.addChild(activities.get(activityNdx));
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ task.addChild(activities.get(activityNdx));
+ }
}
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Restored task=" + task);
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 91aa48e..498fc5c 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -57,6 +57,7 @@
import com.android.internal.util.ToBooleanFunction;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.util.function.pooled.PooledPredicate;
+import com.android.server.wm.LaunchParamsController.LaunchParams;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -1072,11 +1073,17 @@
* @see #getOrCreateRootTask(int, int, boolean)
*/
Task getOrCreateRootTask(@Nullable ActivityRecord r,
- @Nullable ActivityOptions options, @Nullable Task candidateTask, int activityType,
- boolean onTop) {
- // First preference is the windowing mode in the activity options if set.
- int windowingMode = (options != null)
- ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED;
+ @Nullable ActivityOptions options, @Nullable Task candidateTask,
+ @Nullable LaunchParams launchParams, int activityType, boolean onTop) {
+ int windowingMode = WINDOWING_MODE_UNDEFINED;
+ if (launchParams != null) {
+ // If launchParams isn't null, windowing mode is already resolved.
+ windowingMode = launchParams.mWindowingMode;
+ } else if (options != null) {
+ // If launchParams is null and options isn't let's use the windowing mode in the
+ // options.
+ windowingMode = options.getLaunchWindowingMode();
+ }
// Validate that our desired windowingMode will work under the current conditions.
// UNDEFINED windowing mode is a valid result and means that the new root task will inherit
// it's display's windowing mode.
diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java
index 855dd7e..b8d2feb 100644
--- a/services/core/java/com/android/server/wm/TaskPersister.java
+++ b/services/core/java/com/android/server/wm/TaskPersister.java
@@ -328,10 +328,19 @@
// mWriteQueue.add(new TaskWriteQueueItem(task));
final int taskId = task.mTaskId;
- if (mService.mRootWindowContainer.anyTaskForId(taskId,
+ final boolean persistedTask = task.hasActivity();
+ if (persistedTask && mRecentTasks.getTask(taskId) != null) {
+ // The persisted task is added into hierarchy and will also be
+ // added to recent tasks later. So this task should not exist
+ // in recent tasks before it is added.
+ Slog.wtf(TAG, "Existing persisted task with taskId " + taskId
+ + " found");
+ } else if (!persistedTask
+ && mService.mRootWindowContainer.anyTaskForId(taskId,
MATCH_ATTACHED_TASK_OR_RECENT_TASKS) != null) {
// Should not happen.
- Slog.wtf(TAG, "Existing task with taskId " + taskId + "found");
+ Slog.wtf(TAG, "Existing task with taskId " + taskId
+ + " found");
} else if (userId != task.mUserId) {
// Should not happen.
Slog.wtf(TAG, "Task with userId " + task.mUserId + " found in "
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index ffd6d21..9245f8c 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -113,7 +113,7 @@
}
/**
- * @return true if the width or height has changed since last updating resizing window.
+ * @return true if the width or height has changed since last reported to the client.
*/
boolean didFrameSizeChange() {
return (mLastFrame.width() != mFrame.width()) || (mLastFrame.height() != mFrame.height());
@@ -135,13 +135,6 @@
}
/**
- * @return true if the width or height has changed since last reported to the client.
- */
- boolean isFrameSizeChangeReported() {
- return mFrameSizeChanged || didFrameSizeChange();
- }
-
- /**
* Resets the size changed flags so they're all set to false again. This should be called
* after the frames are reported to client.
*/
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b95674e..73b0555 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -84,8 +84,6 @@
import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN;
-import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN;
-import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BOOT;
@@ -433,7 +431,7 @@
* @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
*/
public static boolean sEnableRemoteKeyguardAnimation =
- SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);
+ SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, true);
private static final String DISABLE_TRIPLE_BUFFERING_PROPERTY =
"ro.sf.disable_triple_buffer";
@@ -2234,6 +2232,11 @@
final DisplayContent dc = win.getDisplayContent();
+ if (win.mPendingPositionChanged != null) {
+ win.mPendingPositionChanged.updateLeashPosition(frameNumber);
+ win.mPendingPositionChanged = null;
+ }
+
if (mUseBLASTSync && win.useBLASTSync() && viewVisibility != View.GONE) {
win.prepareDrawHandlers();
result |= RELAYOUT_RES_BLAST_SYNC;
@@ -8633,14 +8636,16 @@
final WindowState win = windowForClientLocked(session, window, false);
if (win == null) {
Slog.w(TAG, "Failed to generate DisplayHash. Invalid window");
- sendDisplayHashError(callback, DISPLAY_HASH_ERROR_MISSING_WINDOW);
+ mDisplayHashController.sendDisplayHashError(callback,
+ DISPLAY_HASH_ERROR_MISSING_WINDOW);
return;
}
DisplayContent displayContent = win.getDisplayContent();
if (displayContent == null) {
Slog.w(TAG, "Failed to generate DisplayHash. Window is not on a display");
- sendDisplayHashError(callback, DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN);
+ mDisplayHashController.sendDisplayHashError(callback,
+ DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN);
return;
}
@@ -8650,7 +8655,8 @@
if (boundsInDisplay.isEmpty()) {
Slog.w(TAG, "Failed to generate DisplayHash. Bounds are not on screen");
- sendDisplayHashError(callback, DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN);
+ mDisplayHashController.sendDisplayHashError(callback,
+ DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN);
return;
}
}
@@ -8660,23 +8666,13 @@
// be covering it with the same uid. We want to make sure we include content that's
// covering to ensure we get as close as possible to what the user sees
final int uid = session.mUid;
- SurfaceControl.LayerCaptureArgs args =
+ SurfaceControl.LayerCaptureArgs.Builder args =
new SurfaceControl.LayerCaptureArgs.Builder(displaySurfaceControl)
.setUid(uid)
- .setSourceCrop(boundsInDisplay)
- .build();
+ .setSourceCrop(boundsInDisplay);
- SurfaceControl.ScreenshotHardwareBuffer screenshotHardwareBuffer =
- SurfaceControl.captureLayers(args);
- if (screenshotHardwareBuffer == null
- || screenshotHardwareBuffer.getHardwareBuffer() == null) {
- Slog.w(TAG, "Failed to generate DisplayHash. Couldn't capture content");
- sendDisplayHashError(callback, DISPLAY_HASH_ERROR_UNKNOWN);
- return;
- }
-
- mDisplayHashController.generateDisplayHash(screenshotHardwareBuffer.getHardwareBuffer(),
- boundsInWindow, hashAlgorithm, callback);
+ mDisplayHashController.generateDisplayHash(args, boundsInWindow,
+ hashAlgorithm, callback);
}
boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) {
@@ -8694,10 +8690,4 @@
return snapshot != null && snapshot.hasImeSurface();
}
}
-
- private void sendDisplayHashError(RemoteCallback callback, int errorCode) {
- Bundle bundle = new Bundle();
- bundle.putInt(EXTRA_DISPLAY_HASH_ERROR_CODE, errorCode);
- callback.sendResult(bundle);
- }
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 7ebc1cc..2eadcd5 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -726,6 +726,8 @@
*/
private InsetsState mFrozenInsetsState;
+ @Nullable InsetsSourceProvider mPendingPositionChanged;
+
private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
private KeyInterceptionInfo mKeyInterceptionInfo;
@@ -772,12 +774,6 @@
updateSurfacePosition(t);
};
- private final Consumer<SurfaceControl.Transaction> mSetSurfacePositionConsumer = t -> {
- if (mSurfaceControl != null && mSurfaceControl.isValid()) {
- t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
- }
- };
-
/**
* @see #setSurfaceTranslationY(int)
*/
@@ -2133,8 +2129,6 @@
: getTask().getWindowConfiguration().hasMovementAnimations();
if (mToken.okToAnimate()
&& (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
- && !mWindowFrames.didFrameSizeChange()
- && !surfaceInsetsChanging()
&& !isDragResizing()
&& hasMovementAnimation
&& !mWinAnimator.mLastHidden
@@ -5324,17 +5318,13 @@
// prior to the rotation.
if (!mSurfaceAnimator.hasLeash() && mPendingSeamlessRotate == null
&& !mLastSurfacePosition.equals(mSurfacePosition)) {
- final boolean frameSizeChanged = mWindowFrames.isFrameSizeChangeReported();
- final boolean surfaceInsetsChanged = surfaceInsetsChanging();
- final boolean surfaceSizeChanged = frameSizeChanged || surfaceInsetsChanged;
+ t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y);
- if (surfaceInsetsChanged) {
+ if (surfaceInsetsChanging() && mWinAnimator.hasSurface()) {
mLastSurfaceInsets.set(mAttrs.surfaceInsets);
- }
- if (surfaceSizeChanged) {
- applyWithNextDraw(mSetSurfacePositionConsumer);
- } else {
- mSetSurfacePositionConsumer.accept(t);
+ t.deferTransactionUntil(mSurfaceControl,
+ mWinAnimator.mSurfaceController.mSurfaceControl,
+ getFrameNumber());
}
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 577f3f5..ffae3ab 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8817,6 +8817,9 @@
return null;
}
final ComponentName supervisorComponent = ComponentName.unflattenFromString(supervisor);
+ if (supervisorComponent == null) {
+ return null;
+ }
if (supervisorComponent.equals(doComponent) || supervisorComponent.equals(
poComponent)) {
return supervisorComponent;
@@ -14696,24 +14699,25 @@
private void sendNetworkLoggingNotificationLocked() {
ensureLocked();
- final ActiveAdmin activeAdmin = getNetworkLoggingControllingAdminLocked();
- if (activeAdmin == null || !activeAdmin.isNetworkLoggingEnabled) {
+ // Send a network logging notification if the admin is a device owner, not profile owner.
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ if (deviceOwner == null || !deviceOwner.isNetworkLoggingEnabled) {
return;
}
- if (activeAdmin.numNetworkLoggingNotifications
+ if (deviceOwner.numNetworkLoggingNotifications
>= ActiveAdmin.DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN) {
return;
}
final long now = System.currentTimeMillis();
- if (now - activeAdmin.lastNetworkLoggingNotificationTimeMs < MS_PER_DAY) {
+ if (now - deviceOwner.lastNetworkLoggingNotificationTimeMs < MS_PER_DAY) {
return;
}
- activeAdmin.numNetworkLoggingNotifications++;
- if (activeAdmin.numNetworkLoggingNotifications
+ deviceOwner.numNetworkLoggingNotifications++;
+ if (deviceOwner.numNetworkLoggingNotifications
>= ActiveAdmin.DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN) {
- activeAdmin.lastNetworkLoggingNotificationTimeMs = 0;
+ deviceOwner.lastNetworkLoggingNotificationTimeMs = 0;
} else {
- activeAdmin.lastNetworkLoggingNotificationTimeMs = now;
+ deviceOwner.lastNetworkLoggingNotificationTimeMs = now;
}
final PackageManagerInternal pm = mInjector.getPackageManagerInternal();
final Intent intent = new Intent(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG);
@@ -14733,7 +14737,7 @@
.bigText(mContext.getString(R.string.network_logging_notification_text)))
.build();
mInjector.getNotificationManager().notify(SystemMessage.NOTE_NETWORK_LOGGING, notification);
- saveSettingsLocked(activeAdmin.getUserHandle().getIdentifier());
+ saveSettingsLocked(deviceOwner.getUserHandle().getIdentifier());
}
/**
@@ -15476,10 +15480,8 @@
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller));
- String currentMode = mInjector.settingsGlobalGetString(PRIVATE_DNS_MODE);
- if (currentMode == null) {
- currentMode = ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
- }
+ final String currentMode =
+ ConnectivityManager.getPrivateDnsMode(mContext.getContentResolver());
switch (currentMode) {
case ConnectivityManager.PRIVATE_DNS_MODE_OFF:
return PRIVATE_DNS_MODE_OFF;
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index c38d0b3..e3fbedd 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -86,6 +86,9 @@
static constexpr auto maxBindDelay = 10000s;
static constexpr auto bindDelayMultiplier = 10;
static constexpr auto bindDelayJitterDivider = 10;
+
+ // Max interval after system invoked the DL when readlog collection can be enabled.
+ static constexpr auto readLogsMaxInterval = 2h;
};
static const Constants& constants() {
@@ -290,6 +293,14 @@
::rmdir(path::c_str(root));
}
+void IncrementalService::IncFsMount::setReadLogsEnabled(bool value) {
+ if (value) {
+ flags |= StorageFlags::ReadLogsEnabled;
+ } else {
+ flags &= ~StorageFlags::ReadLogsEnabled;
+ }
+}
+
IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_view rootDir)
: mVold(sm.getVoldService()),
mDataLoaderManager(sm.getDataLoaderManager()),
@@ -406,7 +417,7 @@
}
bool IncrementalService::needStartDataLoaderLocked(IncFsMount& ifs) {
- if (ifs.dataLoaderStub->params().packageName == Constants::systemPackage) {
+ if (ifs.dataLoaderStub->isSystemDataLoader()) {
return true;
}
@@ -658,7 +669,7 @@
return storageId;
}
-bool IncrementalService::startLoading(StorageId storage,
+bool IncrementalService::startLoading(StorageId storageId,
content::pm::DataLoaderParamsParcel&& dataLoaderParams,
const DataLoaderStatusListener& statusListener,
StorageHealthCheckParams&& healthCheckParams,
@@ -666,12 +677,12 @@
const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) {
// Per Uid timeouts.
if (!perUidReadTimeouts.empty()) {
- setUidReadTimeouts(storage, perUidReadTimeouts);
+ setUidReadTimeouts(storageId, perUidReadTimeouts);
}
// Re-initialize DataLoader.
std::unique_lock l(mLock);
- const auto ifs = getIfsLocked(storage);
+ const auto ifs = getIfsLocked(storageId);
if (!ifs) {
return false;
}
@@ -686,6 +697,32 @@
std::move(healthCheckParams), &healthListener);
CHECK(dataLoaderStub);
+ if (dataLoaderStub->isSystemDataLoader()) {
+ // Readlogs from system dataloader (adb) can always be collected.
+ ifs->startLoadingTs = TimePoint::max();
+ } else {
+ // Assign time when installation wants the DL to start streaming.
+ const auto startLoadingTs = mClock->now();
+ ifs->startLoadingTs = startLoadingTs;
+ // Setup a callback to disable the readlogs after max interval.
+ addTimedJob(*mTimedQueue, storageId, Constants::readLogsMaxInterval,
+ [this, storageId, startLoadingTs]() {
+ const auto ifs = getIfs(storageId);
+ if (!ifs) {
+ LOG(WARNING) << "Can't disable the readlogs, invalid storageId: "
+ << storageId;
+ return;
+ }
+ if (ifs->startLoadingTs != startLoadingTs) {
+ LOG(INFO) << "Can't disable the readlogs, timestamp mismatch (new "
+ "installation?): "
+ << storageId;
+ return;
+ }
+ setStorageParams(*ifs, storageId, /*enableReadLogs=*/false);
+ });
+ }
+
return dataLoaderStub->requestStart();
}
@@ -735,11 +772,16 @@
LOG(ERROR) << "setStorageParams failed, invalid storageId: " << storageId;
return -EINVAL;
}
+ return setStorageParams(*ifs, storageId, enableReadLogs);
+}
- const auto& params = ifs->dataLoaderStub->params();
+int IncrementalService::setStorageParams(IncFsMount& ifs, StorageId storageId,
+ bool enableReadLogs) {
+ const auto& params = ifs.dataLoaderStub->params();
if (enableReadLogs) {
- if (!ifs->readLogsAllowed()) {
- LOG(ERROR) << "setStorageParams failed, readlogs disabled for storageId: " << storageId;
+ if (!ifs.readLogsAllowed()) {
+ LOG(ERROR) << "setStorageParams failed, readlogs disallowed for storageId: "
+ << storageId;
return -EPERM;
}
@@ -760,9 +802,19 @@
<< " check failed: " << status.toString8();
return fromBinderStatus(status);
}
+
+ // Check installation time.
+ const auto now = mClock->now();
+ const auto startLoadingTs = ifs.startLoadingTs;
+ if (startLoadingTs <= now && now - startLoadingTs > Constants::readLogsMaxInterval) {
+ LOG(ERROR) << "setStorageParams failed, readlogs can't be enabled at this time, "
+ "storageId: "
+ << storageId;
+ return -EPERM;
+ }
}
- if (auto status = applyStorageParams(*ifs, enableReadLogs); !status.isOk()) {
+ if (auto status = applyStorageParams(ifs, enableReadLogs); !status.isOk()) {
LOG(ERROR) << "applyStorageParams failed: " << status.toString8();
return fromBinderStatus(status);
}
@@ -2222,6 +2274,10 @@
return dataloader;
}
+bool IncrementalService::DataLoaderStub::isSystemDataLoader() const {
+ return (params().packageName == Constants::systemPackage);
+}
+
bool IncrementalService::DataLoaderStub::requestCreate() {
return setTargetStatus(IDataLoaderStatusListener::DATA_LOADER_CREATED);
}
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 4eb5138..bc441c7 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -231,6 +231,7 @@
MountId id() const { return mId.load(std::memory_order_relaxed); }
const content::pm::DataLoaderParamsParcel& params() const { return mParams; }
+ bool isSystemDataLoader() const;
void setHealthListener(StorageHealthCheckParams&& healthCheckParams,
const StorageHealthListener* healthListener);
long elapsedMsSinceOldestPendingRead();
@@ -330,6 +331,7 @@
StorageMap storages;
BindMap bindPoints;
DataLoaderStubPtr dataLoaderStub;
+ TimePoint startLoadingTs = {};
std::atomic<int> nextStorageDirNo{0};
const IncrementalService& incrementalService;
@@ -348,12 +350,7 @@
void disallowReadLogs() { flags &= ~StorageFlags::ReadLogsAllowed; }
int32_t readLogsAllowed() const { return (flags & StorageFlags::ReadLogsAllowed); }
- void setReadLogsEnabled(bool value) {
- if (value)
- flags |= StorageFlags::ReadLogsEnabled;
- else
- flags &= ~StorageFlags::ReadLogsEnabled;
- }
+ void setReadLogsEnabled(bool value);
int32_t readLogsEnabled() const { return (flags & StorageFlags::ReadLogsEnabled); }
static void cleanupFilesystem(std::string_view root);
@@ -411,6 +408,8 @@
IncFsMount::StorageMap::const_iterator storageIt,
std::string_view path) const;
int makeDirs(const IncFsMount& ifs, StorageId storageId, std::string_view path, int mode);
+
+ int setStorageParams(IncFsMount& ifs, StorageId storageId, bool enableReadLogs);
binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs);
int isFileFullyLoadedFromPath(const IncFsMount& ifs, std::string_view filePath) const;
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 25b34b56..bf798273 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -908,7 +908,7 @@
EXPECT_CALL(*mDataLoader, start(_)).Times(6);
EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
- EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(2);
+ EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(3);
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
@@ -1119,7 +1119,7 @@
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
EXPECT_CALL(*mLooper, addFd(MockIncFs::kPendingReadsFd, _, _, _, _)).Times(2);
EXPECT_CALL(*mLooper, removeFd(MockIncFs::kPendingReadsFd)).Times(2);
- EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(4);
+ EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(5);
sp<NiceMock<MockStorageHealthListener>> listener{new NiceMock<MockStorageHealthListener>};
NiceMock<MockStorageHealthListener>* listenerMock = listener.get();
@@ -1292,6 +1292,147 @@
ASSERT_EQ(mDataLoader->setStorageParams(true), -EPERM);
}
+TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndTimedOut) {
+ mVold->setIncFsMountOptionsSuccess();
+ mAppOpsManager->checkPermissionSuccess();
+
+ const auto readLogsMaxInterval = 2h;
+
+ EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
+ EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+ // Enabling and then disabling readlogs.
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(2);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(1);
+ // After setIncFsMountOptions succeeded expecting to start watching.
+ EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
+ // Not expecting callback removal.
+ EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
+ EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(1);
+ TemporaryDir tempDir;
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
+ ASSERT_GE(storageId, 0);
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+ {}, {}));
+
+ // Disable readlogs callback present.
+ ASSERT_EQ(storageId, mTimedQueue->mId);
+ ASSERT_EQ(mTimedQueue->mAfter, readLogsMaxInterval);
+ auto callback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(storageId);
+
+ ASSERT_GE(mDataLoader->setStorageParams(true), 0);
+ // Now advance clock for 1hr.
+ mClock->advance(1h);
+ ASSERT_GE(mDataLoader->setStorageParams(true), 0);
+ // Now call the timed callback, it should turn off the readlogs.
+ callback();
+ // Now advance clock for 2hrs.
+ mClock->advance(readLogsMaxInterval);
+ ASSERT_EQ(mDataLoader->setStorageParams(true), -EPERM);
+}
+
+TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndNoTimedOutForSystem) {
+ mVold->setIncFsMountOptionsSuccess();
+ mAppOpsManager->checkPermissionSuccess();
+
+ const auto readLogsMaxInterval = 2h;
+
+ EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
+ EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+ // Enabling and then disabling readlogs.
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(3);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(0);
+ // After setIncFsMountOptions succeeded expecting to start watching.
+ EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
+ // Not expecting callback removal.
+ EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
+ EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(0);
+ // System data loader.
+ mDataLoaderParcel.packageName = "android";
+ TemporaryDir tempDir;
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
+ ASSERT_GE(storageId, 0);
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+ {}, {}));
+
+ // No readlogs callback.
+ ASSERT_EQ(mTimedQueue->mAfter, 0ms);
+ ASSERT_EQ(mTimedQueue->mWhat, nullptr);
+
+ ASSERT_GE(mDataLoader->setStorageParams(true), 0);
+ // Now advance clock for 1hr.
+ mClock->advance(1h);
+ ASSERT_GE(mDataLoader->setStorageParams(true), 0);
+ // Now advance clock for 2hrs.
+ mClock->advance(readLogsMaxInterval);
+ ASSERT_EQ(mDataLoader->setStorageParams(true), 0);
+}
+
+TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndNewInstall) {
+ mVold->setIncFsMountOptionsSuccess();
+ mAppOpsManager->checkPermissionSuccess();
+
+ const auto readLogsMaxInterval = 2h;
+
+ EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(2);
+ EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+ // Enabling and then disabling readlogs.
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(3);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(1);
+ // After setIncFsMountOptions succeeded expecting to start watching.
+ EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
+ // Not expecting callback removal.
+ EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
+ EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(2);
+ TemporaryDir tempDir;
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
+ ASSERT_GE(storageId, 0);
+
+ auto dataLoaderParcel = mDataLoaderParcel;
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(dataLoaderParcel), {}, {},
+ {}, {}));
+
+ // Disable readlogs callback present.
+ ASSERT_EQ(storageId, mTimedQueue->mId);
+ ASSERT_EQ(mTimedQueue->mAfter, readLogsMaxInterval);
+ auto callback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(storageId);
+
+ ASSERT_GE(mDataLoader->setStorageParams(true), 0);
+ // Now advance clock for 1.5hrs.
+ mClock->advance(90min);
+ ASSERT_GE(mDataLoader->setStorageParams(true), 0);
+
+ // New installation.
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+ {}, {}));
+
+ // New callback present.
+ ASSERT_EQ(storageId, mTimedQueue->mId);
+ ASSERT_EQ(mTimedQueue->mAfter, readLogsMaxInterval);
+ auto callback2 = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(storageId);
+
+ // Old callback should not disable readlogs (setIncFsMountOptions should be called only once).
+ callback();
+ // Advance clock for another 1.5hrs.
+ mClock->advance(90min);
+ // Still success even it's 3hrs past first install.
+ ASSERT_GE(mDataLoader->setStorageParams(true), 0);
+
+ // New one should disable.
+ callback2();
+ // And timeout.
+ mClock->advance(90min);
+ ASSERT_EQ(mDataLoader->setStorageParams(true), -EPERM);
+}
+
TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndPermissionChanged) {
mVold->setIncFsMountOptionsSuccess();
mAppOpsManager->checkPermissionSuccess();
@@ -1675,7 +1816,7 @@
EXPECT_CALL(*mDataLoader, start(_)).Times(1);
EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
EXPECT_CALL(*mIncFs, setUidReadTimeouts(_, _)).Times(0);
- EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(0);
+ EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(1);
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
int storageId =
@@ -1702,6 +1843,9 @@
// Empty storage.
mIncFs->countFilledBlocksEmpty();
+ // Mark DataLoader as 'system' so that readlogs don't pollute the timed queue.
+ mDataLoaderParcel.packageName = "android";
+
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 5fbf1c4..4e23609 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -116,6 +116,7 @@
import com.android.server.clipboard.ClipboardService;
import com.android.server.compat.PlatformCompat;
import com.android.server.compat.PlatformCompatNative;
+import com.android.server.connectivity.PacProxyService;
import com.android.server.contentcapture.ContentCaptureManagerInternal;
import com.android.server.coverage.CoverageService;
import com.android.server.devicepolicy.DevicePolicyManagerService;
@@ -1315,6 +1316,7 @@
ConsumerIrService consumerIr = null;
MmsServiceBroker mmsService = null;
HardwarePropertiesManagerService hardwarePropertiesService = null;
+ PacProxyService pacProxyService = null;
boolean disableSystemTextClassifier = SystemProperties.getBoolean(
"config.disable_systemtextclassifier", false);
@@ -1874,6 +1876,15 @@
t.traceEnd();
}
+ t.traceBegin("StartPacProxyService");
+ try {
+ pacProxyService = new PacProxyService(context);
+ ServiceManager.addService(Context.PAC_PROXY_SERVICE, pacProxyService);
+ } catch (Throwable e) {
+ reportWtf("starting PacProxyService", e);
+ }
+ t.traceEnd();
+
t.traceBegin("StartConnectivityService");
// This has to be called after NetworkManagementService, NetworkStatsService
// and NetworkPolicyManager because ConnectivityService needs to take these
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index f2e85a7..28940b3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -27,6 +27,7 @@
import static android.app.AlarmManager.RTC_WAKEUP;
import static android.app.AlarmManager.WINDOW_EXACT;
import static android.app.AlarmManager.WINDOW_HEURISTIC;
+import static android.app.AppOpsManager.OP_SCHEDULE_EXACT_ALARM;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
@@ -47,17 +48,20 @@
import static com.android.server.alarm.AlarmManagerService.ACTIVE_INDEX;
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.APP_STANDBY_BUCKET_CHANGED;
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.CHARGING_STATUS_CHANGED;
+import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_EXACT_ALARMS;
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_FOR_CANCELED;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_COMPAT_WINDOW;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_QUOTA;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WINDOW;
+import static com.android.server.alarm.AlarmManagerService.Constants.KEY_CRASH_NON_CLOCK_APPS;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LAZY_BATCHING;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LISTENER_TIMEOUT;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MAX_INTERVAL;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_FUTURITY;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_INTERVAL;
+import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_WINDOW;
import static com.android.server.alarm.AlarmManagerService.FREQUENT_INDEX;
import static com.android.server.alarm.AlarmManagerService.INDEFINITE_DELAY;
import static com.android.server.alarm.AlarmManagerService.IS_WAKEUP_MASK;
@@ -71,6 +75,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -81,6 +86,7 @@
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import android.Manifest;
import android.app.ActivityManager;
@@ -408,7 +414,7 @@
ArgumentCaptor<IAppOpsCallback> appOpsCallbackCaptor = ArgumentCaptor.forClass(
IAppOpsCallback.class);
try {
- verify(mIAppOpsService).startWatchingMode(eq(AppOpsManager.OP_SCHEDULE_EXACT_ALARM),
+ verify(mIAppOpsService).startWatchingMode(eq(OP_SCHEDULE_EXACT_ALARM),
isNull(), appOpsCallbackCaptor.capture());
} catch (RemoteException e) {
// Not expected on a mock.
@@ -445,12 +451,12 @@
private void setTestAlarm(int type, long triggerTime, PendingIntent operation, long interval,
int flags, int callingUid) {
- setTestAlarm(type, triggerTime, operation, interval, flags, callingUid, null);
+ setTestAlarm(type, triggerTime, 0, operation, interval, flags, callingUid, null);
}
- private void setTestAlarm(int type, long triggerTime, PendingIntent operation, long interval,
- int flags, int callingUid, Bundle idleOptions) {
- mService.setImpl(type, triggerTime, WINDOW_EXACT, interval, operation, null, "test", flags,
+ private void setTestAlarm(int type, long triggerTime, long windowLength,
+ PendingIntent operation, long interval, int flags, int callingUid, Bundle idleOptions) {
+ mService.setImpl(type, triggerTime, windowLength, interval, operation, null, "test", flags,
null, null, callingUid, TEST_CALLING_PACKAGE, idleOptions);
}
@@ -572,6 +578,7 @@
setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_COMPAT_WINDOW, 35);
setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION, 40);
setDeviceConfigLong(KEY_LISTENER_TIMEOUT, 45);
+ setDeviceConfigLong(KEY_MIN_WINDOW, 50);
assertEquals(5, mService.mConstants.MIN_FUTURITY);
assertEquals(10, mService.mConstants.MIN_INTERVAL);
assertEquals(15, mService.mConstants.MAX_INTERVAL);
@@ -581,6 +588,7 @@
assertEquals(35, mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW);
assertEquals(40, mService.mConstants.ALLOW_WHILE_IDLE_WHITELIST_DURATION);
assertEquals(45, mService.mConstants.LISTENER_TIMEOUT);
+ assertEquals(50, mService.mConstants.MIN_WINDOW);
}
@Test
@@ -1644,6 +1652,10 @@
getNewMockPendingIntent(), null, null, null,
mock(AlarmManager.AlarmClockInfo.class));
+ // exact
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
+ 0, getNewMockPendingIntent(), null, null, null, null);
+
// exact, allow-while-idle
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
FLAG_ALLOW_WHILE_IDLE, getNewMockPendingIntent(), null, null, null, null);
@@ -1658,6 +1670,22 @@
}
@Test
+ public void exactBinderCallWhenChangeDisabled() throws Exception {
+ doReturn(false).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
+ 0, alarmPi, null, null, null, null);
+
+ verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
+ eq(alarmPi), isNull(), isNull(),
+ eq(FLAG_STANDALONE), isNull(), isNull(),
+ eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), isNull());
+ }
+
+ @Test
public void exactAllowWhileIdleBinderCallWhenChangeDisabled() throws Exception {
doReturn(false).when(
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
@@ -1746,6 +1774,86 @@
}
@Test
+ public void alarmClockBinderCallWithoutPermission() throws RemoteException {
+ setDeviceConfigBoolean(KEY_CRASH_NON_CLOCK_APPS, true);
+ doReturn(true).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
+ () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
+ when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
+
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class);
+ try {
+ mBinder.set(TEST_CALLING_PACKAGE, RTC_WAKEUP, 1234, WINDOW_EXACT, 0, 0,
+ alarmPi, null, null, null, alarmClock);
+ fail("alarm clock binder call succeeded without permission");
+ } catch (SecurityException se) {
+ // Expected.
+ }
+
+ verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
+ verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
+ }
+
+ @Test
+ public void exactBinderCallWithPermission() throws RemoteException {
+ doReturn(true).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ // Permission check is granted by default by the mock.
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
+ 0, alarmPi, null, null, null, null);
+
+ verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
+ verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
+
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
+ eq(alarmPi), isNull(), isNull(),
+ eq(FLAG_STANDALONE), isNull(), isNull(),
+ eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
+
+ final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue());
+ final int type = idleOptions.getTemporaryAppAllowlistType();
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
+ }
+
+ @Test
+ public void exactBinderCallWithAllowlist() throws RemoteException {
+ doReturn(true).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+ // If permission is denied, only then allowlist will be checked.
+ doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
+ () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
+ when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
+
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
+ 0, alarmPi, null, null, null, null);
+
+ verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
+ verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(Process.myUid()));
+
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
+ eq(alarmPi), isNull(), isNull(),
+ eq(FLAG_STANDALONE), isNull(), isNull(),
+ eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
+ System.out.println("what got captured: " + bundleCaptor.getValue());
+ }
+
+ @Test
public void exactAllowWhileIdleBinderCallWithPermission() throws RemoteException {
doReturn(true).when(
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
@@ -1798,48 +1906,57 @@
final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue());
final int type = idleOptions.getTemporaryAppAllowlistType();
- assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
+ // App is on power allowlist, doesn't need explicit FGS grant in broadcast options.
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED, type);
}
@Test
- public void inexactAllowWhileIdleBinderCallWithAllowlist() throws RemoteException {
+ public void exactBinderCallsWithoutPermissionWithoutAllowlist() throws RemoteException {
+ setDeviceConfigBoolean(KEY_CRASH_NON_CLOCK_APPS, true);
doReturn(true).when(
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
anyString(), any(UserHandle.class)));
- when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
- final PendingIntent alarmPi = getNewMockPendingIntent();
- mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 4321, WINDOW_HEURISTIC, 0,
- FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
-
- verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
- Manifest.permission.SCHEDULE_EXACT_ALARM), never());
- verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(Process.myUid()));
-
- final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
- verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(4321L), anyLong(), eq(0L),
- eq(alarmPi), isNull(), isNull(), eq(FLAG_ALLOW_WHILE_IDLE_COMPAT), isNull(),
- isNull(), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
-
- final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue());
- final int type = idleOptions.getTemporaryAppAllowlistType();
- assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
- }
-
- @Test
- public void inexactAllowWhileIdleBinderCallWithoutAllowlist() throws RemoteException {
- doReturn(true).when(
- () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
- anyString(), any(UserHandle.class)));
-
+ doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
+ () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(false);
+
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ try {
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
+ 0, alarmPi, null, null, null, null);
+ fail("exact binder call succeeded without permission");
+ } catch (SecurityException se) {
+ // Expected.
+ }
+ try {
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
+ FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
+ fail("exact, allow-while-idle binder call succeeded without permission");
+ } catch (SecurityException se) {
+ // Expected.
+ }
+ verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM), times(2));
+ verify(mDeviceIdleInternal, times(2)).isAppOnWhitelist(anyInt());
+ }
+
+ @Test
+ public void inexactAllowWhileIdleBinderCall() throws RemoteException {
+ // Both permission and power exemption status don't matter for these alarms.
+ // We only want to test that the flags and idleOptions are correct.
+ doReturn(true).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
final PendingIntent alarmPi = getNewMockPendingIntent();
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 4321, WINDOW_HEURISTIC, 0,
FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
Manifest.permission.SCHEDULE_EXACT_ALARM), never());
- verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(Process.myUid()));
+ verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(4321L), anyLong(), eq(0L),
@@ -1852,13 +1969,104 @@
}
@Test
+ public void binderCallWithUserAllowlist() throws RemoteException {
+ doReturn(true).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
+ () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
+ when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
+ when(mAppStateTracker.isUidPowerSaveUserExempt(Process.myUid())).thenReturn(true);
+
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
+ FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
+
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
+ eq(alarmPi), isNull(), isNull(),
+ eq(FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | FLAG_STANDALONE), isNull(), isNull(),
+ eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), isNull());
+ }
+
+ @Test
+ public void minWindow() {
+ final long minWindow = 73;
+ setDeviceConfigLong(KEY_MIN_WINDOW, minWindow);
+
+ // 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC.
+ for (int window = 1; window <= minWindow; window++) {
+ final PendingIntent pi = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME, 0, window, pi, 0, 0, TEST_CALLING_UID, null);
+
+ assertEquals(1, mService.mAlarmStore.size());
+ final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0);
+ assertEquals(minWindow, a.windowLength);
+ }
+ }
+
+ @Test
+ public void opScheduleExactAlarmRevoked() throws Exception {
+ when(mIAppOpsService.checkOperation(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID,
+ TEST_CALLING_PACKAGE)).thenReturn(AppOpsManager.MODE_ERRORED);
+ mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
+ assertAndHandleMessageSync(REMOVE_EXACT_ALARMS);
+ verify(mService).removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID,
+ TEST_CALLING_PACKAGE);
+ }
+
+ @Test
+ public void removeExactAlarmsOnPermissionRevoked() {
+ doReturn(true).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ // basic exact alarm
+ setTestAlarm(ELAPSED_REALTIME, 0, 0, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID,
+ null);
+ // exact and allow-while-idle alarm
+ setTestAlarm(ELAPSED_REALTIME, 0, 0, getNewMockPendingIntent(), 0, FLAG_ALLOW_WHILE_IDLE,
+ TEST_CALLING_UID, null);
+ // alarm clock
+ setWakeFromIdle(RTC_WAKEUP, 0, getNewMockPendingIntent());
+
+ final PendingIntent inexact = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME, 0, 10, inexact, 0, 0, TEST_CALLING_UID, null);
+
+ final PendingIntent inexactAwi = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME, 0, 10, inexactAwi, 0, FLAG_ALLOW_WHILE_IDLE,
+ TEST_CALLING_UID, null);
+
+ final PendingIntent exactButDifferentUid = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME, 0, 0, exactButDifferentUid, 0, 0, TEST_CALLING_UID + 5,
+ null);
+ assertEquals(6, mService.mAlarmStore.size());
+
+ mService.removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID, TEST_CALLING_PACKAGE);
+
+ final ArrayList<Alarm> remaining = mService.mAlarmStore.asList();
+ assertEquals(3, remaining.size());
+ assertTrue("Basic inexact alarm removed",
+ remaining.removeIf(a -> a.matches(inexact, null)));
+ assertTrue("Inexact allow-while-idle alarm removed",
+ remaining.removeIf(a -> a.matches(inexactAwi, null)));
+ assertTrue("Alarm from different uid removed",
+ remaining.removeIf(a -> a.matches(exactButDifferentUid, null)));
+
+ // Mock should return false by default.
+ verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(TEST_CALLING_UID));
+ }
+
+ @Test
public void idleOptionsSentOnExpiration() throws Exception {
final long triggerTime = mNowElapsedTest + 5000;
final PendingIntent alarmPi = getNewMockPendingIntent();
final Bundle idleOptions = new Bundle();
idleOptions.putChar("TEST_CHAR_KEY", 'x');
idleOptions.putInt("TEST_INT_KEY", 53);
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi, 0, 0, TEST_CALLING_UID,
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, 0, alarmPi, 0, 0, TEST_CALLING_UID,
idleOptions);
mNowElapsedTest = mTestTimer.getElapsed();
@@ -1885,6 +2093,7 @@
assertTrue(i + "th PendingIntent missing: ",
alarmsBefore.removeIf(a -> a.matches(pi, null)));
}
+ assertEquals(BatchingAlarmStore.TAG, mService.mAlarmStore.getName());
setDeviceConfigBoolean(KEY_LAZY_BATCHING, true);
@@ -1895,6 +2104,7 @@
assertTrue(i + "th PendingIntent missing: ",
alarmsAfter.removeIf(a -> a.matches(pi, null)));
}
+ assertEquals(LazyAlarmStore.TAG, mService.mAlarmStore.getName());
}
@After
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 29db740..7e4bc1e 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
@@ -1880,6 +1880,144 @@
}
@Test
+ public void testIsWithinEJQuotaLocked_NeverApp() {
+ JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_NeverApp", 1);
+ setStandbyBucket(NEVER_INDEX, js);
+ synchronized (mQuotaController.mLock) {
+ assertFalse(mQuotaController.isWithinEJQuotaLocked(js));
+ }
+ }
+
+ @Test
+ public void testIsWithinEJQuotaLocked_Charging() {
+ setCharging();
+ JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_Charging", 1);
+ synchronized (mQuotaController.mLock) {
+ assertTrue(mQuotaController.isWithinEJQuotaLocked(js));
+ }
+ }
+
+ @Test
+ public void testIsWithinEJQuotaLocked_UnderDuration() {
+ setDischarging();
+ JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_UnderDuration", 1);
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
+ synchronized (mQuotaController.mLock) {
+ assertTrue(mQuotaController.isWithinEJQuotaLocked(js));
+ }
+ }
+
+ @Test
+ public void testIsWithinEJQuotaLocked_OverDuration() {
+ setDischarging();
+ JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_OverDuration", 1);
+ setStandbyBucket(FREQUENT_INDEX, js);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 10 * MINUTE_IN_MILLIS);
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), true);
+ synchronized (mQuotaController.mLock) {
+ assertFalse(mQuotaController.isWithinEJQuotaLocked(js));
+ }
+ }
+
+ @Test
+ public void testIsWithinEJQuotaLocked_TimingSession() {
+ setDischarging();
+ setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 20 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 15 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 13 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 8 * MINUTE_IN_MILLIS);
+
+ JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_TimingSession", 1);
+ for (int i = 0; i < 25; ++i) {
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - ((60 - i) * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS,
+ 2), true);
+
+ synchronized (mQuotaController.mLock) {
+ setStandbyBucket(ACTIVE_INDEX, js);
+ assertEquals("Active has incorrect quota status with " + (i + 1) + " sessions",
+ i < 19, mQuotaController.isWithinEJQuotaLocked(js));
+
+ setStandbyBucket(WORKING_INDEX, js);
+ assertEquals("Working has incorrect quota status with " + (i + 1) + " sessions",
+ i < 14, mQuotaController.isWithinEJQuotaLocked(js));
+
+ setStandbyBucket(FREQUENT_INDEX, js);
+ assertEquals("Frequent has incorrect quota status with " + (i + 1) + " sessions",
+ i < 12, mQuotaController.isWithinEJQuotaLocked(js));
+
+ setStandbyBucket(RARE_INDEX, js);
+ assertEquals("Rare has incorrect quota status with " + (i + 1) + " sessions",
+ i < 9, mQuotaController.isWithinEJQuotaLocked(js));
+
+ setStandbyBucket(RESTRICTED_INDEX, js);
+ assertEquals("Restricted has incorrect quota status with " + (i + 1) + " sessions",
+ i < 7, mQuotaController.isWithinEJQuotaLocked(js));
+ }
+ }
+ }
+
+ /**
+ * Tests that Timers properly track sessions when an app is added and removed from the temp
+ * allowlist.
+ */
+ @Test
+ public void testIsWithinEJQuotaLocked_TempAllowlisting() {
+ setDischarging();
+ JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_TempAllowlisting", 1);
+ setStandbyBucket(FREQUENT_INDEX, js);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 10 * MINUTE_IN_MILLIS);
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), true);
+ synchronized (mQuotaController.mLock) {
+ assertFalse(mQuotaController.isWithinEJQuotaLocked(js));
+ }
+
+ 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);
+
+ // Apps on the temp allowlist should be able to schedule & start EJs, even if they're out
+ // of quota (as long as they are in the temp allowlist grace period).
+ mTempAllowlistListener.onAppAdded(mSourceUid);
+ synchronized (mQuotaController.mLock) {
+ assertTrue(mQuotaController.isWithinEJQuotaLocked(js));
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mTempAllowlistListener.onAppRemoved(mSourceUid);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ // Still in grace period
+ synchronized (mQuotaController.mLock) {
+ assertTrue(mQuotaController.isWithinEJQuotaLocked(js));
+ }
+ advanceElapsedClock(6 * SECOND_IN_MILLIS);
+ // Out of grace period.
+ synchronized (mQuotaController.mLock) {
+ assertFalse(mQuotaController.isWithinEJQuotaLocked(js));
+ }
+ }
+
+ @Test
public void testMaybeScheduleCleanupAlarmLocked() {
// No sessions saved yet.
synchronized (mQuotaController.mLock) {
@@ -3881,6 +4019,55 @@
}
@Test
+ public void testGetRemainingEJExecutionTimeLocked_IncrementalTimingSessions() {
+ setDischarging();
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 20 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 15 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 13 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 5 * MINUTE_IN_MILLIS);
+
+ for (int i = 1; i <= 25; ++i) {
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - ((60 - i) * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS,
+ 2), true);
+
+ synchronized (mQuotaController.mLock) {
+ setStandbyBucket(ACTIVE_INDEX);
+ assertEquals("Active has incorrect remaining EJ time with " + i + " sessions",
+ (20 - i) * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ setStandbyBucket(WORKING_INDEX);
+ assertEquals("Working has incorrect remaining EJ time with " + i + " sessions",
+ (15 - i) * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ setStandbyBucket(FREQUENT_INDEX);
+ assertEquals("Frequent has incorrect remaining EJ time with " + i + " sessions",
+ (13 - i) * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ setStandbyBucket(RARE_INDEX);
+ assertEquals("Rare has incorrect remaining EJ time with " + i + " sessions",
+ (10 - i) * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ setStandbyBucket(RESTRICTED_INDEX);
+ assertEquals("Restricted has incorrect remaining EJ time with " + i + " sessions",
+ (5 - i) * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+ }
+ }
+
+ @Test
public void testGetTimeUntilEJQuotaConsumedLocked_NoHistory() {
final long[] limits = mQuotaController.getEJLimitsMs();
for (int i = 0; i < limits.length; ++i) {
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
index 129d263..e13597d 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
@@ -19,23 +19,37 @@
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.assertTrue;
import static org.junit.Assert.fail;
import android.app.ActivityManager;
+import android.app.ActivityManager.OnUidImportanceListener;
import android.app.ActivityManager.RecentTaskInfo;
+import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.IActivityManager;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.DropBoxManager;
+import android.os.Handler;
import android.os.IBinder;
import android.os.IRemoteCallback;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.support.test.uiautomator.UiDevice;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.text.TextUtils;
+import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.FlakyTest;
@@ -65,6 +79,12 @@
private static final long AWAIT_TIMEOUT = 2000;
private static final long CHECK_INTERVAL = 100;
+ private static final String TEST_FGS_CLASS =
+ "com.android.servicestests.apps.simpleservicetestapp.SimpleFgService";
+ private static final String ACTION_FGS_STATS_TEST =
+ "com.android.servicestests.apps.simpleservicetestapp.ACTION_FGS_STATS_TEST";
+ private static final String EXTRA_MESSENGER = "extra_messenger";
+
private IActivityManager mService;
private IRemoteCallback mCallback;
private Context mContext;
@@ -204,4 +224,184 @@
public void onServiceDisconnected(ComponentName name) {
}
}
+
+ /**
+ * Note: This test actually only works in eng build. It'll always pass
+ * in user and userdebug build, because the expected exception won't be
+ * thrown in those builds.
+ */
+ @LargeTest
+ @Test
+ public void testFgsProcStatsTracker() throws Exception {
+ final PackageManager pm = mContext.getPackageManager();
+ final long timeout = 5000;
+ int uid = pm.getPackageUid(TEST_APP, 0);
+ final MyUidImportanceListener uidListener1 = new MyUidImportanceListener(uid);
+ final MyUidImportanceListener uidListener2 = new MyUidImportanceListener(uid);
+ final ActivityManager am = mContext.getSystemService(ActivityManager.class);
+ final CountDownLatch[] latchHolder = new CountDownLatch[1];
+ final H handler = new H(Looper.getMainLooper(), latchHolder);
+ final Messenger messenger = new Messenger(handler);
+ final DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class);
+ final CountDownLatch dboxLatch = new CountDownLatch(1);
+ final BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String tag_wtf = "system_server_wtf";
+ if (tag_wtf.equals(intent.getStringExtra(DropBoxManager.EXTRA_TAG))) {
+ final DropBoxManager.Entry e = dbox.getNextEntry(tag_wtf, intent.getLongExtra(
+ DropBoxManager.EXTRA_TIME, 0) - 1);
+ final String text = e.getText(8192);
+ if (TextUtils.isEmpty(text)) {
+ return;
+ }
+ if (text.indexOf("can't store negative values") == -1) {
+ return;
+ }
+ dboxLatch.countDown();
+ }
+ }
+ };
+ try {
+ mContext.registerReceiver(receiver,
+ new IntentFilter(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED));
+ am.addOnUidImportanceListener(uidListener1,
+ RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
+ am.addOnUidImportanceListener(uidListener2, RunningAppProcessInfo.IMPORTANCE_GONE);
+ runShellCommand("cmd deviceidle whitelist +" + TEST_APP);
+ toggleScreenOn(true);
+
+ final Intent intent = new Intent(ACTION_FGS_STATS_TEST);
+ final ComponentName cn = ComponentName.unflattenFromString(
+ TEST_APP + "/" + TEST_FGS_CLASS);
+ final Bundle bundle = new Bundle();
+ intent.setComponent(cn);
+ bundle.putBinder(EXTRA_MESSENGER, messenger.getBinder());
+ intent.putExtras(bundle);
+
+ latchHolder[0] = new CountDownLatch(1);
+ mContext.startForegroundService(intent);
+ assertTrue("Timed out to start fg service", uidListener1.waitFor(
+ RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE, timeout));
+ assertTrue("Timed out to get the remote messenger", latchHolder[0].await(
+ timeout, TimeUnit.MILLISECONDS));
+
+ Thread.sleep(timeout);
+ latchHolder[0] = new CountDownLatch(1);
+ handler.sendRemoteMessage(H.MSG_STOP_FOREGROUND, 0, 0, null);
+ assertTrue("Timed out to wait for stop fg", latchHolder[0].await(
+ timeout, TimeUnit.MILLISECONDS));
+
+ Thread.sleep(timeout);
+ latchHolder[0] = new CountDownLatch(1);
+ handler.sendRemoteMessage(H.MSG_START_FOREGROUND, 0, 0, null);
+ assertTrue("Timed out to wait for start fg", latchHolder[0].await(
+ timeout, TimeUnit.MILLISECONDS));
+
+ toggleScreenOn(false);
+ latchHolder[0] = new CountDownLatch(1);
+ handler.sendRemoteMessage(H.MSG_STOP_FOREGROUND, 0, 0, null);
+ assertTrue("Timed out to wait for stop fg", latchHolder[0].await(
+ timeout, TimeUnit.MILLISECONDS));
+ assertFalse("There shouldn't be negative values", dboxLatch.await(
+ timeout * 2, TimeUnit.MILLISECONDS));
+ } finally {
+ toggleScreenOn(true);
+ runShellCommand("cmd deviceidle whitelist -" + TEST_APP);
+ am.removeOnUidImportanceListener(uidListener1);
+ am.removeOnUidImportanceListener(uidListener2);
+ am.forceStopPackage(TEST_APP);
+ mContext.unregisterReceiver(receiver);
+ }
+ }
+
+ /**
+ * Make sure the screen state.
+ */
+ private void toggleScreenOn(final boolean screenon) throws Exception {
+ if (screenon) {
+ runShellCommand("input keyevent KEYCODE_WAKEUP");
+ runShellCommand("wm dismiss-keyguard");
+ } else {
+ runShellCommand("input keyevent KEYCODE_SLEEP");
+ }
+ // Since the screen on/off intent is ordered, they will not be sent right now.
+ Thread.sleep(2_000);
+ }
+
+ private class H extends Handler {
+ static final int MSG_INIT = 0;
+ static final int MSG_DONE = 1;
+ static final int MSG_START_FOREGROUND = 2;
+ static final int MSG_STOP_FOREGROUND = 3;
+
+ private Messenger mRemoteMessenger;
+ private CountDownLatch[] mLatchHolder;
+
+ H(Looper looper, CountDownLatch[] latchHolder) {
+ super(looper);
+ mLatchHolder = latchHolder;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_INIT:
+ mRemoteMessenger = (Messenger) msg.obj;
+ mLatchHolder[0].countDown();
+ break;
+ case MSG_DONE:
+ mLatchHolder[0].countDown();
+ break;
+ }
+ }
+
+ void sendRemoteMessage(int what, int arg1, int arg2, Object obj) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.arg1 = arg1;
+ msg.arg2 = arg2;
+ msg.obj = obj;
+ try {
+ mRemoteMessenger.send(msg);
+ } catch (RemoteException e) {
+ }
+ msg.recycle();
+ }
+ }
+
+ private static class MyUidImportanceListener implements OnUidImportanceListener {
+ final CountDownLatch[] mLatchHolder = new CountDownLatch[1];
+ private final int mExpectedUid;
+ private int mExpectedImportance;
+ private int mCurrentImportance = RunningAppProcessInfo.IMPORTANCE_GONE;
+
+ MyUidImportanceListener(int uid) {
+ mExpectedUid = uid;
+ }
+
+ @Override
+ public void onUidImportance(int uid, int importance) {
+ if (uid == mExpectedUid) {
+ synchronized (this) {
+ if (importance == mExpectedImportance && mLatchHolder[0] != null) {
+ mLatchHolder[0].countDown();
+ }
+ mCurrentImportance = importance;
+ }
+ Log.i(TAG, "uid " + uid + " importance: " + importance);
+ }
+ }
+
+ boolean waitFor(int expectedImportance, long timeout) throws Exception {
+ synchronized (this) {
+ mExpectedImportance = expectedImportance;
+ if (mCurrentImportance == expectedImportance) {
+ return true;
+ }
+ mLatchHolder[0] = new CountDownLatch(1);
+ }
+ return mLatchHolder[0].await(timeout, TimeUnit.MILLISECONDS);
+ }
+ }
}
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 6890ed1..c84c1cf 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
@@ -415,9 +415,7 @@
+ AppSearchImpl.CHECK_OPTIMIZE_INTERVAL;
i++) {
GenericDocument document =
- new GenericDocument.Builder<>("uri" + i, "type")
- .setNamespace("namespace")
- .build();
+ new GenericDocument.Builder<>("namespace", "uri" + i, "type").build();
mAppSearchImpl.putDocument("package", "database", document);
}
@@ -477,7 +475,7 @@
// Insert document
GenericDocument document =
- new GenericDocument.Builder<>("uri", "type").setNamespace("namespace").build();
+ new GenericDocument.Builder<>("namespace", "uri", "type").build();
mAppSearchImpl.putDocument("package", "database", document);
// Rewrite SearchSpec
@@ -517,11 +515,11 @@
// Insert documents
GenericDocument document1 =
- new GenericDocument.Builder<>("uri", "typeA").setNamespace("namespace").build();
+ new GenericDocument.Builder<>("namespace", "uri", "typeA").build();
mAppSearchImpl.putDocument("package", "database1", document1);
GenericDocument document2 =
- new GenericDocument.Builder<>("uri", "typeB").setNamespace("namespace").build();
+ new GenericDocument.Builder<>("namespace", "uri", "typeB").build();
mAppSearchImpl.putDocument("package", "database2", document2);
// Rewrite SearchSpec
@@ -561,7 +559,7 @@
// Insert document
GenericDocument document =
- new GenericDocument.Builder<>("uri", "type").setNamespace("namespace").build();
+ new GenericDocument.Builder<>("namespace", "uri", "type").build();
mAppSearchImpl.putDocument("package", "database", document);
// If 'allowedPrefixedSchemas' is empty, this returns false since there's nothing to
@@ -614,7 +612,7 @@
// Insert package1 document
GenericDocument document =
- new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
+ new GenericDocument.Builder<>("namespace", "uri", "schema1").build();
mAppSearchImpl.putDocument("package1", "database1", document);
// No query filters specified, package2 shouldn't be able to query for package1's documents.
@@ -625,14 +623,13 @@
assertThat(searchResultPage.getResults()).isEmpty();
// Insert package2 document
- document =
- new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
+ document = new GenericDocument.Builder<>("namespace", "uri", "schema2").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);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document);
}
/**
@@ -665,7 +662,7 @@
// Insert package1 document
GenericDocument document =
- new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
+ new GenericDocument.Builder<>("namespace", "uri", "schema1").build();
mAppSearchImpl.putDocument("package1", "database1", document);
// "package1" filter specified, but package2 shouldn't be able to query for package1's
@@ -680,8 +677,7 @@
assertThat(searchResultPage.getResults()).isEmpty();
// Insert package2 document
- document =
- new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
+ document = new GenericDocument.Builder<>("namespace", "uri", "schema2").build();
mAppSearchImpl.putDocument("package2", "database2", document);
// "package2" filter specified, package2 should only get its own documents back.
@@ -692,7 +688,7 @@
.build();
searchResultPage = mAppSearchImpl.query("package2", "database2", "", searchSpec);
assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document);
}
@Test
@@ -1073,7 +1069,7 @@
for (SearchResult result : searchResultPage.getResults()) {
assertThat(result.getPackageName()).isEqualTo("com.package.foo");
assertThat(result.getDatabaseName()).isEqualTo("databaseName");
- assertThat(result.getDocument())
+ assertThat(result.getGenericDocument())
.isEqualTo(
GenericDocumentToProtoConverter.toGenericDocument(
strippedDocumentProto.build()));
@@ -1128,9 +1124,7 @@
appSearchImpl.putDocument(
"package",
"database",
- new GenericDocument.Builder<>("uri", "type")
- .setNamespace("namespace")
- .build());
+ new GenericDocument.Builder<>("namespace", "uri", "type").build());
});
expectThrows(
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
index 194be37..70e1e05 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
@@ -36,23 +36,23 @@
private static final byte[] BYTE_ARRAY_2 = new byte[] {(byte) 4, (byte) 5, (byte) 6, (byte) 7};
private static final GenericDocument DOCUMENT_PROPERTIES_1 =
new GenericDocument.Builder<GenericDocument.Builder<?>>(
- "sDocumentProperties1", "sDocumentPropertiesSchemaType1")
+ "namespace", "sDocumentProperties1", "sDocumentPropertiesSchemaType1")
.setCreationTimestampMillis(12345L)
.build();
private static final GenericDocument DOCUMENT_PROPERTIES_2 =
new GenericDocument.Builder<GenericDocument.Builder<?>>(
- "sDocumentProperties2", "sDocumentPropertiesSchemaType2")
+ "namespace", "sDocumentProperties2", "sDocumentPropertiesSchemaType2")
.setCreationTimestampMillis(6789L)
.build();
@Test
public void testDocumentProtoConvert() {
GenericDocument document =
- new GenericDocument.Builder<GenericDocument.Builder<?>>("uri1", "schemaType1")
+ new GenericDocument.Builder<GenericDocument.Builder<?>>(
+ "namespace", "uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setScore(1)
.setTtlMillis(1L)
- .setNamespace("namespace")
.setPropertyLong("longKey1", 1L)
.setPropertyDouble("doubleKey1", 1.0)
.setPropertyBoolean("booleanKey1", true)
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 0b1c120..d07211f 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
@@ -93,10 +93,10 @@
assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString);
assertThat(match.getFullText()).isEqualTo(propertyValueString);
assertThat(match.getExactMatch()).isEqualTo(exactMatch);
- assertThat(match.getExactMatchPosition())
+ assertThat(match.getExactMatchRange())
.isEqualTo(new SearchResult.MatchRange(/*lower=*/ 29, /*upper=*/ 32));
assertThat(match.getFullText()).isEqualTo(propertyValueString);
- assertThat(match.getSnippetPosition())
+ assertThat(match.getSnippetRange())
.isEqualTo(new SearchResult.MatchRange(/*lower=*/ 26, /*upper=*/ 32));
assertThat(match.getSnippet()).isEqualTo(window);
}
@@ -210,20 +210,20 @@
SearchResult.MatchInfo match1 = result.getMatches().get(0);
assertThat(match1.getPropertyPath()).isEqualTo("sender.name");
assertThat(match1.getFullText()).isEqualTo("Test Name Jr.");
- assertThat(match1.getExactMatchPosition())
+ assertThat(match1.getExactMatchRange())
.isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 4));
assertThat(match1.getExactMatch()).isEqualTo("Test");
- assertThat(match1.getSnippetPosition())
+ assertThat(match1.getSnippetRange())
.isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 9));
assertThat(match1.getSnippet()).isEqualTo("Test Name");
SearchResult.MatchInfo match2 = result.getMatches().get(1);
assertThat(match2.getPropertyPath()).isEqualTo("sender.email");
assertThat(match2.getFullText()).isEqualTo("TestNameJr@gmail.com");
- assertThat(match2.getExactMatchPosition())
+ assertThat(match2.getExactMatchRange())
.isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 20));
assertThat(match2.getExactMatch()).isEqualTo("TestNameJr@gmail.com");
- assertThat(match2.getSnippetPosition())
+ assertThat(match2.getSnippetRange())
.isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 20));
assertThat(match2.getSnippet()).isEqualTo("TestNameJr@gmail.com");
}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java b/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
new file mode 100644
index 0000000..7afcbf7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appsearch.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.SparseIntArray;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.appsearch.external.localstorage.MockPackageManager;
+import com.android.server.appsearch.external.localstorage.stats.CallStats;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class PlatformLoggerTest {
+ private static final int TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS = 100;
+ private static final int TEST_DEFAULT_SAMPLING_RATIO = 10;
+ private static final String TEST_PACKAGE_NAME = "packageName";
+ private MockPackageManager mMockPackageManager = new MockPackageManager();
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ Context context = ApplicationProvider.getApplicationContext();
+ mContext =
+ new ContextWrapper(context) {
+ @Override
+ public PackageManager getPackageManager() {
+ return mMockPackageManager.getMockPackageManager();
+ }
+ };
+ }
+
+ @Test
+ public void testcreateExtraStatsLocked_nullSamplingRatioMap_returnsDefaultSamplingRatio() {
+ PlatformLogger logger = new PlatformLogger(
+ ApplicationProvider.getApplicationContext(),
+ UserHandle.USER_NULL,
+ new PlatformLogger.Config(
+ TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
+ TEST_DEFAULT_SAMPLING_RATIO,
+ /*samplingRatioMap=*/ null));
+
+ // Make sure default sampling ratio is used if samplingMap is not provided.
+ assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+ CallStats.CALL_TYPE_UNKNOWN).mSamplingRatio).isEqualTo(
+ TEST_DEFAULT_SAMPLING_RATIO);
+ assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+ CallStats.CALL_TYPE_INITIALIZE).mSamplingRatio).isEqualTo(
+ TEST_DEFAULT_SAMPLING_RATIO);
+ assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+ CallStats.CALL_TYPE_QUERY).mSamplingRatio).isEqualTo(
+ TEST_DEFAULT_SAMPLING_RATIO);
+ assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+ CallStats.CALL_TYPE_FLUSH).mSamplingRatio).isEqualTo(
+ TEST_DEFAULT_SAMPLING_RATIO);
+ }
+
+
+ @Test
+ public void testcreateExtraStatsLocked_with_samplingRatioMap_returnsConfiguredSamplingRatio() {
+ int putDocumentSamplingRatio = 1;
+ int querySamplingRatio = 2;
+ final SparseIntArray samplingRatios = new SparseIntArray();
+ samplingRatios.put(CallStats.CALL_TYPE_PUT_DOCUMENT, putDocumentSamplingRatio);
+ samplingRatios.put(CallStats.CALL_TYPE_QUERY, querySamplingRatio);
+ PlatformLogger logger = new PlatformLogger(
+ ApplicationProvider.getApplicationContext(),
+ UserHandle.USER_NULL,
+ new PlatformLogger.Config(
+ TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
+ TEST_DEFAULT_SAMPLING_RATIO,
+ samplingRatios));
+
+ // The default sampling ratio should be used if no sampling ratio is
+ // provided for certain call type.
+ assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+ CallStats.CALL_TYPE_INITIALIZE).mSamplingRatio).isEqualTo(
+ TEST_DEFAULT_SAMPLING_RATIO);
+ assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+ CallStats.CALL_TYPE_FLUSH).mSamplingRatio).isEqualTo(
+ TEST_DEFAULT_SAMPLING_RATIO);
+
+ // The configured sampling ratio is used if sampling ratio is available
+ // for certain call type.
+ assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+ CallStats.CALL_TYPE_PUT_DOCUMENT).mSamplingRatio).isEqualTo(
+ putDocumentSamplingRatio);
+ assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+ CallStats.CALL_TYPE_QUERY).mSamplingRatio).isEqualTo(
+ querySamplingRatio);
+ }
+
+ @Test
+ public void testShouldLogForTypeLocked_trueWhenSampleRatioIsOne() {
+ final int samplingRatio = 1;
+ final String testPackageName = "packageName";
+ PlatformLogger logger = new PlatformLogger(
+ ApplicationProvider.getApplicationContext(),
+ UserHandle.USER_NULL,
+ new PlatformLogger.Config(
+ TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
+ samplingRatio,
+ /* samplingMap=*/ null));
+
+ // Sample should always be logged for the first time if sampling is disabled(value is one).
+ assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isTrue();
+ assertThat(logger.createExtraStatsLocked(testPackageName,
+ CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(0);
+ }
+
+ @Test
+ public void testShouldLogForTypeLocked_falseWhenSampleRatioIsNegative() {
+ final int samplingRatio = -1;
+ final String testPackageName = "packageName";
+ PlatformLogger logger = new PlatformLogger(
+ ApplicationProvider.getApplicationContext(),
+ UserHandle.USER_NULL,
+ new PlatformLogger.Config(
+ TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
+ samplingRatio,
+ /* samplingMap=*/ null));
+
+ // Makes sure sample will be excluded due to sampling if sample ratio is negative.
+ assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isFalse();
+ // Skipped count should be 0 since it doesn't pass the sampling.
+ assertThat(logger.createExtraStatsLocked(testPackageName,
+ CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(0);
+ }
+
+ @Test
+ public void testShouldLogForTypeLocked_falseWhenWithinCoolOffInterval() {
+ // Next sample won't be excluded due to sampling.
+ final int samplingRatio = 1;
+ // Next sample would guaranteed to be too close.
+ final int minTimeIntervalBetweenSamplesMillis = Integer.MAX_VALUE;
+ final String testPackageName = "packageName";
+ PlatformLogger logger = new PlatformLogger(
+ ApplicationProvider.getApplicationContext(),
+ UserHandle.USER_NULL,
+ new PlatformLogger.Config(
+ minTimeIntervalBetweenSamplesMillis,
+ samplingRatio,
+ /* samplingMap=*/ null));
+ logger.setLastPushTimeMillisLocked(SystemClock.elapsedRealtime());
+
+ // Makes sure sample will be excluded due to rate limiting if samples are too close.
+ assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isFalse();
+ assertThat(logger.createExtraStatsLocked(testPackageName,
+ CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(1);
+ }
+
+ @Test
+ public void testShouldLogForTypeLocked_trueWhenOutsideOfCoolOffInterval() {
+ // Next sample won't be excluded due to sampling.
+ final int samplingRatio = 1;
+ // Next sample would guaranteed to be included.
+ final int minTimeIntervalBetweenSamplesMillis = 0;
+ final String testPackageName = "packageName";
+ PlatformLogger logger = new PlatformLogger(
+ ApplicationProvider.getApplicationContext(),
+ UserHandle.USER_NULL,
+ new PlatformLogger.Config(
+ minTimeIntervalBetweenSamplesMillis,
+ samplingRatio,
+ /* samplingMap=*/ null));
+ logger.setLastPushTimeMillisLocked(SystemClock.elapsedRealtime());
+
+ // Makes sure sample will be logged if it is not too close to previous sample.
+ assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isTrue();
+ assertThat(logger.createExtraStatsLocked(testPackageName,
+ CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(0);
+ }
+
+ /** Makes sure the caching works while getting the UID for calling package. */
+ @Test
+ public void testGetPackageUidAsUser() throws Exception {
+ final String testPackageName = "packageName";
+ final int testUid = 1234;
+ PlatformLogger logger = new PlatformLogger(
+ mContext,
+ mContext.getUserId(),
+ new PlatformLogger.Config(
+ TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
+ TEST_DEFAULT_SAMPLING_RATIO,
+ /* samplingMap=*/ null));
+ mMockPackageManager.mockGetPackageUidAsUser(testPackageName, mContext.getUserId(), testUid);
+
+ //
+ // First time, no cache
+ //
+ PlatformLogger.ExtraStats extraStats = logger.createExtraStatsLocked(testPackageName,
+ CallStats.CALL_TYPE_PUT_DOCUMENT);
+
+ verify(mMockPackageManager.getMockPackageManager(), times(1)).getPackageUidAsUser(
+ eq(testPackageName), /*userId=*/ anyInt());
+ assertThat(extraStats.mPackageUid).isEqualTo(testUid);
+
+ //
+ // Second time, we have cache
+ //
+ extraStats = logger.createExtraStatsLocked(testPackageName,
+ CallStats.CALL_TYPE_PUT_DOCUMENT);
+
+ // Count is still one since we will use the cache
+ verify(mMockPackageManager.getMockPackageManager(), times(1)).getPackageUidAsUser(
+ eq(testPackageName), /*userId=*/ anyInt());
+ assertThat(extraStats.mPackageUid).isEqualTo(testUid);
+
+ //
+ // Remove the cache and try again
+ //
+ assertThat(logger.removeCachedUidForPackage(testPackageName)).isEqualTo(testUid);
+ extraStats = logger.createExtraStatsLocked(testPackageName,
+ CallStats.CALL_TYPE_PUT_DOCUMENT);
+
+ // count increased by 1 since cache is cleared
+ verify(mMockPackageManager.getMockPackageManager(), times(2)).getPackageUidAsUser(
+ eq(testPackageName), /*userId=*/ anyInt());
+ assertThat(extraStats.mPackageUid).isEqualTo(testUid);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 54825ee..c1b6101 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -65,6 +65,7 @@
@Mock HysteresisLevels mScreenBrightnessThresholds;
@Mock Handler mNoOpHandler;
@Mock DisplayDevice mDisplayDevice;
+ @Mock HighBrightnessModeController mHbmController;
private static final int LIGHT_SENSOR_WARMUP_TIME = 0;
@Before
@@ -90,7 +91,8 @@
BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE,
INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG,
DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
- mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mLogicalDisplay, mContext
+ mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mLogicalDisplay,
+ mContext, mHbmController
);
controller.setLoggingEnabled(true);
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index 893ce9e..bdf94f3 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -81,6 +81,7 @@
public class BrightnessTrackerTest {
private static final float DEFAULT_INITIAL_BRIGHTNESS = 2.5f;
private static final boolean DEFAULT_COLOR_SAMPLING_ENABLED = true;
+ private static final String DEFAULT_DISPLAY_ID = "123";
private static final float FLOAT_DELTA = 0.01f;
private BrightnessTracker mTracker;
@@ -285,18 +286,20 @@
@Test
public void testBrightnessEvent() {
- final int brightness = 20;
+ final float brightness = 0.5f;
+ final String displayId = "1234";
startTracker(mTracker);
mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f));
mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2));
- notifyBrightnessChanged(mTracker, brightness);
+ notifyBrightnessChanged(mTracker, brightness, displayId);
List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
mTracker.stop();
assertEquals(1, events.size());
BrightnessChangeEvent event = events.get(0);
assertEquals(mInjector.currentTimeMillis(), event.timeStamp);
+ assertEquals(displayId, event.uniqueDisplayId);
assertEquals(1, event.luxValues.length);
assertEquals(1.0f, event.luxValues[0], FLOAT_DELTA);
assertEquals(mInjector.currentTimeMillis() - TimeUnit.SECONDS.toMillis(2),
@@ -314,6 +317,7 @@
public void testBrightnessFullPopulatedEvent() {
final int initialBrightness = 230;
final int brightness = 130;
+ final String displayId = "1234";
mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1);
mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3333);
@@ -326,7 +330,7 @@
batteryChangeEvent(30, 60));
mInjector.mSensorListener.onSensorChanged(createSensorEvent(1000.0f));
final long sensorTime = mInjector.currentTimeMillis();
- notifyBrightnessChanged(mTracker, brightness);
+ notifyBrightnessChanged(mTracker, brightness, displayId);
List<BrightnessChangeEvent> eventsNoPackage
= mTracker.getEvents(0, false).getList();
List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
@@ -335,6 +339,7 @@
assertEquals(1, events.size());
BrightnessChangeEvent event = events.get(0);
assertEquals(event.timeStamp, mInjector.currentTimeMillis());
+ assertEquals(displayId, event.uniqueDisplayId);
assertArrayEquals(new float[] {1000.0f}, event.luxValues, 0.01f);
assertArrayEquals(new long[] {sensorTime}, event.luxTimestamps);
assertEquals(brightness, event.brightness, FLOAT_DELTA);
@@ -364,7 +369,7 @@
final int systemUpdatedBrightness = 20;
notifyBrightnessChanged(mTracker, systemUpdatedBrightness, false /*userInitiated*/,
0.5f /*powerBrightnessFactor(*/, false /*isUserSetBrightness*/,
- false /*isDefaultBrightnessConfig*/);
+ false /*isDefaultBrightnessConfig*/, DEFAULT_DISPLAY_ID);
List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
// No events because we filtered out our change.
assertEquals(0, events.size());
@@ -455,6 +460,7 @@
+ "batteryLevel=\"1.0\" nightMode=\"false\" colorTemperature=\"0\" "
+ "reduceBrightColors=\"false\" reduceBrightColorsStrength=\"40\" "
+ "reduceBrightColorsOffset=\"0\"\n"
+ + "uniqueDisplayId=\"123\""
+ "lux=\"32.2,31.1\" luxTimestamps=\""
+ Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\""
+ "defaultConfig=\"true\" powerSaveFactor=\"0.5\" userPoint=\"true\" />"
@@ -465,6 +471,7 @@
+ "batteryLevel=\"0.5\" nightMode=\"true\" colorTemperature=\"3235\" "
+ "reduceBrightColors=\"true\" reduceBrightColorsStrength=\"40\" "
+ "reduceBrightColorsOffset=\"0\"\n"
+ + "uniqueDisplayId=\"456\""
+ "lux=\"132.2,131.1\" luxTimestamps=\""
+ Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\""
+ "colorSampleDuration=\"3456\" colorValueBuckets=\"123,598,23,19\"/>"
@@ -476,6 +483,7 @@
+ "batteryLevel=\"1.0\" nightMode=\"false\" colorTemperature=\"0\" "
+ "reduceBrightColors=\"false\" reduceBrightColorsStrength=\"40\" "
+ "reduceBrightColorsOffset=\"0\"\n"
+ + "uniqueDisplayId=\"789\""
+ "lux=\"32.2,31.1\" luxTimestamps=\""
+ Long.toString(twoMonthsAgo) + "," + Long.toString(twoMonthsAgo) + "\"/>"
+ "</events>";
@@ -485,6 +493,7 @@
BrightnessChangeEvent event = events.get(0);
assertEquals(someTimeAgo, event.timeStamp);
assertEquals(194.2, event.brightness, FLOAT_DELTA);
+ assertEquals("123", event.uniqueDisplayId);
assertArrayEquals(new float[] {32.2f, 31.1f}, event.luxValues, FLOAT_DELTA);
assertArrayEquals(new long[] {someTimeAgo, someTimeAgo}, event.luxTimestamps);
assertEquals(32.333, event.lastBrightness, FLOAT_DELTA);
@@ -503,6 +512,7 @@
event = events.get(0);
assertEquals(someTimeAgo, event.timeStamp);
assertEquals(71, event.brightness, FLOAT_DELTA);
+ assertEquals("456", event.uniqueDisplayId);
assertArrayEquals(new float[] {132.2f, 131.1f}, event.luxValues, FLOAT_DELTA);
assertArrayEquals(new long[] {someTimeAgo, someTimeAgo}, event.luxTimestamps);
assertEquals(32, event.lastBrightness, FLOAT_DELTA);
@@ -575,6 +585,7 @@
@Test
public void testWriteThenRead() throws Exception {
final int brightness = 20;
+ final String displayId = "1234";
mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1);
mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3339);
@@ -593,7 +604,7 @@
mInjector.incrementTime(TimeUnit.SECONDS.toMillis(3));
notifyBrightnessChanged(mTracker, brightness, true /*userInitiated*/,
0.5f /*powerBrightnessFactor*/, true /*hasUserBrightnessPoints*/,
- false /*isDefaultBrightnessConfig*/);
+ false /*isDefaultBrightnessConfig*/, displayId);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
mTracker.writeEventsLocked(baos);
mTracker.stop();
@@ -607,6 +618,7 @@
assertEquals(1, events.size());
BrightnessChangeEvent event = events.get(0);
+ assertEquals(displayId, event.uniqueDisplayId);
assertArrayEquals(new float[] {2000.0f, 3000.0f}, event.luxValues, FLOAT_DELTA);
assertArrayEquals(new long[] {firstSensorTime, secondSensorTime}, event.luxTimestamps);
assertEquals(brightness, event.brightness, FLOAT_DELTA);
@@ -678,6 +690,7 @@
builder.setTimeStamp(345L);
builder.setPackageName("com.example");
builder.setUserId(12);
+ builder.setUniqueDisplayId("9876");
float[] luxValues = new float[2];
luxValues[0] = 3000.0f;
luxValues[1] = 4000.0f;
@@ -710,6 +723,7 @@
assertEquals(event.timeStamp, event2.timeStamp);
assertEquals(event.packageName, event2.packageName);
assertEquals(event.userId, event2.userId);
+ assertEquals(event.uniqueDisplayId, event2.uniqueDisplayId);
assertArrayEquals(event.luxValues, event2.luxValues, FLOAT_DELTA);
assertArrayEquals(event.luxTimestamps, event2.luxTimestamps);
assertEquals(event.batteryLevel, event2.batteryLevel, FLOAT_DELTA);
@@ -773,7 +787,7 @@
long eventTime = mInjector.currentTimeMillis();
mTracker.notifyBrightnessChanged(brightness, true /*userInitiated*/,
1.0f /*powerBrightnessFactor*/, false /*isUserSetBrightness*/,
- false /*isDefaultBrightnessConfig*/);
+ false /*isDefaultBrightnessConfig*/, DEFAULT_DISPLAY_ID);
// Time passes before handler can run.
mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2));
@@ -791,6 +805,35 @@
assertEquals(eventTime, event.timeStamp);
}
+ @Test
+ public void testDisplayIdChange() {
+ float firstBrightness = 0.5f;
+ float secondBrightness = 0.75f;
+ String firstDisplayId = "123";
+ String secondDisplayId = "456";
+
+ startTracker(mTracker);
+ mInjector.mSensorListener.onSensorChanged(createSensorEvent(1000.0f));
+
+ notifyBrightnessChanged(mTracker, firstBrightness, firstDisplayId);
+ mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2));
+ List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
+ assertEquals(1, events.size());
+ BrightnessChangeEvent firstEvent = events.get(0);
+ assertEquals(firstDisplayId, firstEvent.uniqueDisplayId);
+ assertEquals(firstBrightness, firstEvent.brightness, 0.001f);
+
+ notifyBrightnessChanged(mTracker, secondBrightness, secondDisplayId);
+ mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2));
+ events = mTracker.getEvents(0, true).getList();
+ assertEquals(2, events.size());
+ BrightnessChangeEvent secondEvent = events.get(1);
+ assertEquals(secondDisplayId, secondEvent.uniqueDisplayId);
+ assertEquals(secondBrightness, secondEvent.brightness, 0.001f);
+
+ mTracker.stop();
+ }
+
private InputStream getInputStream(String data) {
return new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
}
@@ -831,16 +874,21 @@
}
private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness) {
+ notifyBrightnessChanged(tracker, brightness, DEFAULT_DISPLAY_ID);
+ }
+
+ private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness,
+ String displayId) {
notifyBrightnessChanged(tracker, brightness, true /*userInitiated*/,
1.0f /*powerBrightnessFactor*/, false /*isUserSetBrightness*/,
- false /*isDefaultBrightnessConfig*/);
+ false /*isDefaultBrightnessConfig*/, displayId);
}
private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness,
boolean userInitiated, float powerBrightnessFactor, boolean isUserSetBrightness,
- boolean isDefaultBrightnessConfig) {
+ boolean isDefaultBrightnessConfig, String displayId) {
tracker.notifyBrightnessChanged(brightness, userInitiated, powerBrightnessFactor,
- isUserSetBrightness, isDefaultBrightnessConfig);
+ isUserSetBrightness, isDefaultBrightnessConfig, displayId);
mInjector.waitForHandler();
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 47f3bf9..b5336e3 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -45,7 +45,6 @@
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
-import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -667,7 +666,6 @@
@Test
public void initCecVersion_limitToMinimumSupportedVersion() {
mNativeWrapper.setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
- Log.e("MARVIN", "set setting CEC");
mHdmiControlService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.HDMI_CEC_VERSION_2_0);
@@ -679,9 +677,7 @@
@Test
public void initCecVersion_limitToAtLeast1_4() {
- Log.e("MARVIN", "set HAL CEC to 0");
mNativeWrapper.setCecVersion(0x0);
- Log.e("MARVIN", "set setting CEC to 2");
mHdmiControlService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.HDMI_CEC_VERSION_2_0);
@@ -694,7 +690,6 @@
@Test
public void initCecVersion_useHighestMatchingVersion() {
mNativeWrapper.setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_2_0);
- Log.e("MARVIN", "set setting CEC");
mHdmiControlService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.HDMI_CEC_VERSION_2_0);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
index 605f781..53b4b49 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
@@ -19,7 +19,6 @@
import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2;
-import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
import static com.google.common.truth.Truth.assertThat;
@@ -51,6 +50,7 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.concurrent.TimeUnit;
/** Tests for {@link ActiveSourceAction} */
@@ -84,7 +84,8 @@
when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
- mHdmiControlService = new HdmiControlService(mContextSpy) {
+ mHdmiControlService = new HdmiControlService(mContextSpy,
+ Collections.singletonList(HdmiDeviceInfo.DEVICE_TV)) {
@Override
AudioManager getAudioManager() {
return new AudioManager() {
@@ -140,6 +141,7 @@
mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();
+ mNativeWrapper.clearResultMessages();
}
@Test
@@ -152,7 +154,7 @@
mTestLooper.dispatchAll();
HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- ADDR_TV,
+ mTvDevice.mAddress,
ADDR_PLAYBACK_1);
assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
@@ -191,7 +193,7 @@
mTestLooper.dispatchAll();
HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- ADDR_TV,
+ mTvDevice.mAddress,
ADDR_PLAYBACK_1);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
@@ -220,12 +222,12 @@
mTestLooper.dispatchAll();
HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- ADDR_TV,
+ mTvDevice.mAddress,
ADDR_PLAYBACK_1);
assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
HdmiCecMessage giveDevicePowerStatus2 = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- ADDR_TV,
+ mTvDevice.mAddress,
ADDR_PLAYBACK_2);
assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus2);
}
@@ -245,13 +247,13 @@
mTestLooper.dispatchAll();
HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- ADDR_TV,
+ mTvDevice.mAddress,
ADDR_PLAYBACK_1);
assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
HdmiCecMessage giveDevicePowerStatus2 = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- ADDR_TV,
+ mTvDevice.mAddress,
ADDR_PLAYBACK_2);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus2);
@@ -265,7 +267,7 @@
}
private void reportPowerStatus(int logicalAddress, boolean broadcast, int powerStatus) {
- int destination = broadcast ? ADDR_BROADCAST : ADDR_TV;
+ int destination = broadcast ? ADDR_BROADCAST : mTvDevice.mAddress;
HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
logicalAddress, destination,
powerStatus);
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
new file mode 100644
index 0000000..3ab3448
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.dex;
+
+import static org.mockito.Mockito.inOrder;
+
+import com.android.internal.art.ArtStatsLog;
+import com.android.server.pm.dex.ArtStatsLogUtils.ArtStatsLogger;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.InOrder;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * Unit tests for {@link com.android.server.pm.dex.ArtStatsLogUtils}.
+ *
+ * Run with "atest ArtStatsLogUtilsTest".
+ */
+@RunWith(JUnit4.class)
+public final class ArtStatsLogUtilsTest {
+ private static final String TAG = ArtStatsLogUtilsTest.class.getSimpleName();
+ private static final String COMPILER_FILTER = "space-profile";
+ private static final String PROFILE_DEX_METADATA = "primary.prof";
+ private static final String VDEX_DEX_METADATA = "primary.vdex";
+ private static final byte[] DEX_CONTENT = "dexData".getBytes();
+ private static final int COMPILATION_REASON = 1;
+ private static final int RESULT_CODE = 222;
+ private static final int UID = 111;
+ private static final long COMPILE_TIME = 333L;
+ private static final long SESSION_ID = 444L;
+
+ @Mock
+ ArtStatsLogger mockLogger;
+
+ private static Path TEST_DIR;
+ private static Path DEX;
+ private static Path NON_DEX;
+
+ @BeforeClass
+ public static void setUpAll() throws IOException {
+ TEST_DIR = Files.createTempDirectory(null);
+ DEX = Files.createFile(TEST_DIR.resolve("classes.dex"));
+ NON_DEX = Files.createFile(TEST_DIR.resolve("test.dex"));
+ Files.write(DEX, DEX_CONTENT);
+ Files.write(NON_DEX, "empty".getBytes());
+ }
+
+ @AfterClass
+ public static void tearnDownAll() {
+ deleteSliently(DEX);
+ deleteSliently(NON_DEX);
+ deleteSliently(TEST_DIR);
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testProfileAndVdexDexMetadata() throws IOException {
+ // Setup
+ Path dexMetadataPath = null;
+ Path apk = null;
+ try {
+ dexMetadataPath = createDexMetadata(PROFILE_DEX_METADATA, VDEX_DEX_METADATA);
+ apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath);
+
+ // Act
+ ArtStatsLogUtils.writeStatsLog(
+ mockLogger,
+ SESSION_ID,
+ apk.toString(),
+ COMPILER_FILTER,
+ UID,
+ COMPILE_TIME,
+ dexMetadataPath.toString(),
+ COMPILATION_REASON,
+ RESULT_CODE);
+
+ // Assert
+ verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__PROFILE_AND_VDEX);
+ } finally {
+ deleteSliently(dexMetadataPath);
+ deleteSliently(apk);
+ }
+ }
+
+ @Test
+ public void testProfileOnlyDexMetadata() throws IOException {
+ // Setup
+ Path dexMetadataPath = null;
+ Path apk = null;
+ try {
+ dexMetadataPath = createDexMetadata(PROFILE_DEX_METADATA);
+ apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath);
+
+ // Act
+ ArtStatsLogUtils.writeStatsLog(
+ mockLogger,
+ SESSION_ID,
+ apk.toString(),
+ COMPILER_FILTER,
+ UID,
+ COMPILE_TIME,
+ dexMetadataPath.toString(),
+ COMPILATION_REASON,
+ RESULT_CODE);
+
+ // Assert
+ verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__PROFILE);
+ } finally {
+ deleteSliently(dexMetadataPath);
+ deleteSliently(apk);
+ }
+ }
+
+ @Test
+ public void testVdexOnlyDexMetadata() throws IOException {
+ // Setup
+ Path dexMetadataPath = null;
+ Path apk = null;
+ try {
+ dexMetadataPath = createDexMetadata(VDEX_DEX_METADATA);
+ apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath);
+
+ // Act
+ ArtStatsLogUtils.writeStatsLog(
+ mockLogger,
+ SESSION_ID,
+ apk.toString(),
+ COMPILER_FILTER,
+ UID,
+ COMPILE_TIME,
+ dexMetadataPath.toString(),
+ COMPILATION_REASON,
+ RESULT_CODE);
+
+ // Assert
+ verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__VDEX);
+ } finally {
+ deleteSliently(dexMetadataPath);
+ deleteSliently(apk);
+ }
+ }
+
+ @Test
+ public void testNoneDexMetadata() throws IOException {
+ // Setup
+ Path apk = null;
+ try {
+ apk = zipFiles(".apk", DEX, NON_DEX);
+
+ // Act
+ ArtStatsLogUtils.writeStatsLog(
+ mockLogger,
+ SESSION_ID,
+ apk.toString(),
+ COMPILER_FILTER,
+ UID,
+ COMPILE_TIME,
+ /*dexMetadataPath=*/ null,
+ COMPILATION_REASON,
+ RESULT_CODE);
+
+ // Assert
+ verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__NONE_DEX_METADATA);
+ } finally {
+ deleteSliently(apk);
+ }
+ }
+
+ @Test
+ public void testUnKnownDexMetadata() throws IOException {
+ // Setup
+ Path dexMetadataPath = null;
+ Path apk = null;
+ try {
+ dexMetadataPath = createDexMetadata("unknown");
+ apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath);
+
+ // Act
+ ArtStatsLogUtils.writeStatsLog(
+ mockLogger,
+ SESSION_ID,
+ apk.toString(),
+ COMPILER_FILTER,
+ UID,
+ COMPILE_TIME,
+ dexMetadataPath.toString(),
+ COMPILATION_REASON,
+ RESULT_CODE);
+
+ // Assert
+ verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__UNKNOWN_DEX_METADATA);
+ } finally {
+ deleteSliently(dexMetadataPath);
+ deleteSliently(apk);
+ }
+ }
+
+ private void verifyWrites(int dexMetadataType) {
+ InOrder inorder = inOrder(mockLogger);
+ inorder.verify(mockLogger).write(
+ SESSION_ID, UID,
+ COMPILATION_REASON,
+ COMPILER_FILTER,
+ ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_RESULT_CODE,
+ RESULT_CODE,
+ dexMetadataType);
+ inorder.verify(mockLogger).write(
+ SESSION_ID,
+ UID,
+ COMPILATION_REASON,
+ COMPILER_FILTER,
+ ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_DEX_CODE_BYTES,
+ DEX_CONTENT.length,
+ dexMetadataType);
+ inorder.verify(mockLogger).write(
+ SESSION_ID,
+ UID,
+ COMPILATION_REASON,
+ COMPILER_FILTER,
+ ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME,
+ COMPILE_TIME,
+ dexMetadataType);
+ }
+
+ private Path zipFiles(String suffix, Path... files) throws IOException {
+ Path zipFile = Files.createTempFile(null, suffix);
+ try (final OutputStream os = Files.newOutputStream(zipFile)) {
+ try (final ZipOutputStream zos = new ZipOutputStream(os)) {
+ for (Path file : files) {
+ ZipEntry zipEntry = new ZipEntry(file.getFileName().toString());
+ zos.putNextEntry(zipEntry);
+ zos.write(Files.readAllBytes(file));
+ zos.closeEntry();
+ }
+ }
+ }
+ return zipFile;
+ }
+
+ private Path createDexMetadata(String... entryNames) throws IOException {
+ Path zipFile = Files.createTempFile(null, ".dm");
+ try (final OutputStream os = Files.newOutputStream(zipFile)) {
+ try (final ZipOutputStream zos = new ZipOutputStream(os)) {
+ for (String entryName : entryNames) {
+ ZipEntry zipEntry = new ZipEntry(entryName);
+ zos.putNextEntry(zipEntry);
+ zos.write(entryName.getBytes());
+ zos.closeEntry();
+ }
+ }
+ }
+ return zipFile;
+ }
+
+ private static void deleteSliently(Path file) {
+ if (file != null) {
+ try {
+ Files.deleteIfExists(file);
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index 60390dc..b2dacab 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -261,6 +261,7 @@
// mEndTimeStamp is based on the enclosing IntervalStats, don't bother checking
assertEquals(us1.mLastTimeUsed, us2.mLastTimeUsed);
assertEquals(us1.mLastTimeVisible, us2.mLastTimeVisible);
+ assertEquals(us1.mLastTimeComponentUsed, us2.mLastTimeComponentUsed);
assertEquals(us1.mTotalTimeInForeground, us2.mTotalTimeInForeground);
assertEquals(us1.mTotalTimeVisible, us2.mTotalTimeVisible);
assertEquals(us1.mLastTimeForegroundServiceUsed, us2.mLastTimeForegroundServiceUsed);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index 1b7e1ca..7d5eec0 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -107,7 +107,8 @@
waitForCompletion(thread);
verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId));
- verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.IGNORED));
+ verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId),
+ eq(Vibration.Status.IGNORED_UNSUPPORTED));
}
@Test
@@ -121,7 +122,8 @@
waitForCompletion(thread);
verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId));
- verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.IGNORED));
+ verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId),
+ eq(Vibration.Status.IGNORED_UNSUPPORTED));
}
@Test
@@ -206,8 +208,8 @@
thread.cancel();
waitForCompletion(thread);
- verify(mIBatteryStatsMock, never()).noteVibratorOn(eq(UID), anyLong());
- verify(mIBatteryStatsMock, never()).noteVibratorOff(eq(UID));
+ verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), anyLong());
+ verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
index 8789992..799ec53 100644
--- a/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
@@ -17,9 +17,13 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.servicestests.apps.simpleservicetestapp">
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+
<application>
<service android:name=".SimpleService"
android:exported="true" />
+ <service android:name=".SimpleFgService"
+ android:exported="true" />
</application>
</manifest>
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleFgService.java b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleFgService.java
new file mode 100644
index 0000000..ccfc0b7
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleFgService.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.servicestests.apps.simpleservicetestapp;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.R;
+
+public class SimpleFgService extends Service {
+ private static final String TAG = SimpleFgService.class.getSimpleName();
+ private static final String NOTIFICATION_CHANNEL_ID = TAG;
+ private static final int NOTIFICATION_ID = 1;
+
+ private static final int MSG_INIT = 0;
+ private static final int MSG_DONE = 1;
+ private static final int MSG_START_FOREGROUND = 2;
+ private static final int MSG_STOP_FOREGROUND = 3;
+
+ private static final String ACTION_FGS_STATS_TEST =
+ "com.android.servicestests.apps.simpleservicetestapp.ACTION_FGS_STATS_TEST";
+ private static final String EXTRA_MESSENGER = "extra_messenger";
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_START_FOREGROUND: {
+ Log.i(TAG, "startForeground");
+ startForeground(NOTIFICATION_ID, mNotification);
+ sendRemoteMessage(MSG_DONE, 0, 0, null);
+ } break;
+ case MSG_STOP_FOREGROUND: {
+ Log.i(TAG, "stopForeground");
+ stopForeground(true);
+ sendRemoteMessage(MSG_DONE, 0, 0, null);
+ } break;
+ }
+ }
+ };
+ private final Messenger mMessenger = new Messenger(mHandler);
+
+ private Notification mNotification;
+ private Messenger mRemoteMessenger;
+
+ @Override
+ public void onCreate() {
+ Log.i(TAG, "onCreate");
+ final NotificationManager nm = getSystemService(NotificationManager.class);
+ nm.createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID,
+ NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_LOW));
+ mNotification = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
+ .setContentTitle(TAG)
+ .setSmallIcon(R.drawable.ic_info)
+ .build();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.i(TAG, "onStartCommand");
+ startForeground(NOTIFICATION_ID, mNotification);
+ if (ACTION_FGS_STATS_TEST.equals(intent.getAction())) {
+ mRemoteMessenger = new Messenger(intent.getExtras().getBinder(EXTRA_MESSENGER));
+ sendRemoteMessage(MSG_INIT, 0, 0, mMessenger);
+ }
+ return START_NOT_STICKY;
+ }
+
+ private void sendRemoteMessage(int what, int arg1, int arg2, Object obj) {
+ final Message msg = Message.obtain();
+ msg.what = what;
+ msg.arg1 = arg1;
+ msg.arg2 = arg2;
+ msg.obj = obj;
+ try {
+ mRemoteMessenger.send(msg);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.i(TAG, "onDestroy");
+ mNotification = null;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index e22cda6..eba5634 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -42,16 +42,20 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import android.app.ActivityOptions;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
+import com.android.server.wm.LaunchParamsController.LaunchParams;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -87,6 +91,48 @@
}
@Test
+ public void getOrCreateLaunchRootRespectsResolvedWindowingMode() {
+ final Task rootTask = createTaskStackOnDisplay(
+ WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ rootTask.mCreatedByOrganizer = true;
+ final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
+ taskDisplayArea.setLaunchRootTask(
+ rootTask, new int[]{WINDOWING_MODE_FREEFORM}, new int[]{ACTIVITY_TYPE_STANDARD});
+
+ final Task candidateRootTask = createTaskStackOnDisplay(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
+ final LaunchParams launchParams = new LaunchParams();
+ launchParams.mWindowingMode = WINDOWING_MODE_FREEFORM;
+
+ final Task actualRootTask = taskDisplayArea.getOrCreateRootTask(
+ activity, null /* options */, candidateRootTask,
+ launchParams, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ assertSame(rootTask, actualRootTask.getRootTask());
+ }
+
+ @Test
+ public void getOrCreateLaunchRootUsesActivityOptionsWindowingMode() {
+ final Task rootTask = createTaskStackOnDisplay(
+ WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ rootTask.mCreatedByOrganizer = true;
+ final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
+ taskDisplayArea.setLaunchRootTask(
+ rootTask, new int[]{WINDOWING_MODE_FREEFORM}, new int[]{ACTIVITY_TYPE_STANDARD});
+
+ final Task candidateRootTask = createTaskStackOnDisplay(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
+
+ final Task actualRootTask = taskDisplayArea.getOrCreateRootTask(
+ activity, options, candidateRootTask,
+ null /* launchParams */, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ assertSame(rootTask, actualRootTask.getRootTask());
+ }
+
+ @Test
public void testActivityWithZBoost_taskDisplayAreaDoesNotMoveUp() {
final Task stack = createTaskStackOnDisplay(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 7822a85..ae8e2de 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -100,9 +100,9 @@
}
@Override
- public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
- CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
- int logo, int windowFlags, Configuration overrideConfig, int displayId) {
+ public StartingSurface addSplashScreen(IBinder appToken, int userId, String packageName,
+ int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
+ int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId) {
final com.android.server.wm.WindowState window;
final ActivityRecord activity;
final WindowManagerService wm = mWmSupplier.get();
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerService.java b/services/translation/java/com/android/server/translation/TranslationManagerService.java
index 8874e0a..72e1e33 100644
--- a/services/translation/java/com/android/server/translation/TranslationManagerService.java
+++ b/services/translation/java/com/android/server/translation/TranslationManagerService.java
@@ -27,6 +27,7 @@
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.IBinder;
+import android.os.IRemoteCallback;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
@@ -203,6 +204,28 @@
}
}
+ @Override
+ public void registerUiTranslationStateCallback(IRemoteCallback callback, int userId) {
+ TranslationManagerServiceImpl service;
+ synchronized (mLock) {
+ service = getServiceForUserLocked(userId);
+ }
+ if (service != null) {
+ service.registerUiTranslationStateCallback(callback, Binder.getCallingUid());
+ }
+ }
+
+ @Override
+ public void unregisterUiTranslationStateCallback(IRemoteCallback callback, int userId) {
+ TranslationManagerServiceImpl service;
+ synchronized (mLock) {
+ service = getServiceForUserLocked(userId);
+ }
+ if (service != null) {
+ service.unregisterUiTranslationStateCallback(callback);
+ }
+ }
+
/**
* Dump the service state into the given stream. You run "adb shell dumpsys translation".
*/
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
index ab6ac12..1ca07cb 100644
--- a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
+++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
@@ -17,17 +17,24 @@
package com.android.server.translation;
import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_SUCCESS;
+import static android.view.translation.UiTranslationManager.EXTRA_SOURCE_LOCALE;
+import static android.view.translation.UiTranslationManager.EXTRA_STATE;
+import static android.view.translation.UiTranslationManager.EXTRA_TARGET_LOCALE;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
+import android.os.Bundle;
import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.service.translation.TranslationServiceInfo;
import android.util.Slog;
import android.view.autofill.AutofillId;
+import android.view.inputmethod.InputMethodInfo;
import android.view.translation.TranslationSpec;
import android.view.translation.UiTranslationManager.UiTranslationState;
@@ -36,6 +43,7 @@
import com.android.internal.util.SyncResultReceiver;
import com.android.server.LocalServices;
import com.android.server.infra.AbstractPerUserSystemService;
+import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal.ActivityTokens;
@@ -174,5 +182,50 @@
} catch (RemoteException e) {
Slog.w(TAG, "Update UiTranslationState fail: " + e);
}
+ invokeCallbacks(state, sourceSpec, destSpec);
}
+
+ private void invokeCallbacks(
+ int state, TranslationSpec sourceSpec, TranslationSpec targetSpec) {
+ Bundle res = new Bundle();
+ res.putInt(EXTRA_STATE, state);
+ // TODO(177500482): Store the locale pair so it can be sent for RESUME events.
+ if (sourceSpec != null) {
+ res.putString(EXTRA_SOURCE_LOCALE, sourceSpec.getLanguage());
+ res.putString(EXTRA_TARGET_LOCALE, targetSpec.getLanguage());
+ }
+ // TODO(177500482): Only support the *current* Input Method.
+ List<InputMethodInfo> enabledInputMethods =
+ LocalServices.getService(InputMethodManagerInternal.class)
+ .getEnabledInputMethodListAsUser(mUserId);
+ mCallbacks.broadcast((callback, uid) -> {
+ // Code here is non-optimal since it's temporary..
+ boolean isIme = false;
+ for (InputMethodInfo inputMethod : enabledInputMethods) {
+ if ((int) uid == inputMethod.getServiceInfo().applicationInfo.uid) {
+ isIme = true;
+ }
+ }
+ // TODO(177500482): Invoke it for the application being translated too.
+ if (!isIme) {
+ return;
+ }
+ try {
+ callback.sendResult(res);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to invoke UiTranslationStateCallback: " + e);
+ }
+ });
+ }
+
+ public void registerUiTranslationStateCallback(IRemoteCallback callback, int sourceUid) {
+ mCallbacks.register(callback, sourceUid);
+ // TODO(177500482): trigger the callback here if we're already translating the UI.
+ }
+
+ public void unregisterUiTranslationStateCallback(IRemoteCallback callback) {
+ mCallbacks.unregister(callback);
+ }
+
+ private final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<>();
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProto.java b/services/usage/java/com/android/server/usage/UsageStatsProto.java
index 78b1477..ec4c5fc 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsProto.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsProto.java
@@ -147,6 +147,10 @@
stats.mTotalTimeVisible = proto.readLong(
IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS);
break;
+ case (int) IntervalStatsProto.UsageStats.LAST_TIME_COMPONENT_USED_MS:
+ stats.mLastTimeComponentUsed = statsOut.beginTime + proto.readLong(
+ IntervalStatsProto.UsageStats.LAST_TIME_COMPONENT_USED_MS);
+ break;
}
}
proto.end(token);
@@ -345,6 +349,9 @@
usageStats.mLastTimeVisible, stats.beginTime);
proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS,
usageStats.mTotalTimeVisible);
+ UsageStatsProtoV2.writeOffsetTimestamp(proto,
+ IntervalStatsProto.UsageStats.LAST_TIME_COMPONENT_USED_MS,
+ usageStats.mLastTimeComponentUsed, stats.beginTime);
proto.write(IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT, usageStats.mAppLaunchCount);
try {
writeChooserCounts(proto, usageStats);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
index e6d2841..5c5667c 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
@@ -90,6 +90,10 @@
stats.mTotalTimeVisible = proto.readLong(
UsageStatsObfuscatedProto.TOTAL_TIME_VISIBLE_MS);
break;
+ case (int) UsageStatsObfuscatedProto.LAST_TIME_COMPONENT_USED_MS:
+ stats.mLastTimeComponentUsed = beginTime + proto.readLong(
+ UsageStatsObfuscatedProto.LAST_TIME_COMPONENT_USED_MS);
+ break;
case ProtoInputStream.NO_MORE_FIELDS:
return stats;
}
@@ -312,6 +316,8 @@
writeOffsetTimestamp(proto, UsageStatsObfuscatedProto.LAST_TIME_VISIBLE_MS,
stats.mLastTimeVisible, beginTime);
proto.write(UsageStatsObfuscatedProto.TOTAL_TIME_VISIBLE_MS, stats.mTotalTimeVisible);
+ writeOffsetTimestamp(proto, UsageStatsObfuscatedProto.LAST_TIME_COMPONENT_USED_MS,
+ stats.mLastTimeComponentUsed, beginTime);
proto.write(UsageStatsObfuscatedProto.APP_LAUNCH_COUNT, stats.mAppLaunchCount);
try {
writeChooserCounts(proto, stats);
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index f35b9e2..22b4f4e 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -1003,6 +1003,8 @@
formatElapsedTime(usageStats.mTotalTimeVisible, prettyDates));
pw.printPair("lastTimeVisible",
formatDateTime(usageStats.mLastTimeVisible, prettyDates));
+ pw.printPair("lastTimeComponentUsed",
+ formatDateTime(usageStats.mLastTimeComponentUsed, prettyDates));
pw.printPair("totalTimeFS",
formatElapsedTime(usageStats.mTotalTimeForegroundServiceUsed, prettyDates));
pw.printPair("lastTimeFS",
diff --git a/telecomm/java/android/telecom/CallDiagnosticService.java b/telecomm/java/android/telecom/CallDiagnosticService.java
index 809f2bc..5fb6b33 100644
--- a/telecomm/java/android/telecom/CallDiagnosticService.java
+++ b/telecomm/java/android/telecom/CallDiagnosticService.java
@@ -27,6 +27,8 @@
import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.RemoteException;
+import android.telephony.Annotation;
+import android.telephony.ims.ImsReasonInfo;
import android.util.ArrayMap;
import com.android.internal.telecom.ICallDiagnosticService;
@@ -105,6 +107,12 @@
throws RemoteException {
handleBluetoothCallQualityReport(qualityReport);
}
+
+ @Override
+ public void notifyCallDisconnected(@NonNull String callId,
+ @NonNull DisconnectCause disconnectCause) throws RemoteException {
+ handleCallDisconnected(callId, disconnectCause);
+ }
}
/**
@@ -329,6 +337,32 @@
}
/**
+ * Handles a request from the Telecom framework to get a disconnect message from the
+ * {@link CallDiagnosticService}.
+ * @param callId The ID of the call.
+ * @param disconnectCause The telecom disconnect cause.
+ */
+ private void handleCallDisconnected(@NonNull String callId,
+ @NonNull DisconnectCause disconnectCause) {
+ Log.i(this, "handleCallDisconnected: call=%s; cause=%s", callId, disconnectCause);
+ DiagnosticCall diagnosticCall = mDiagnosticCallByTelecomCallId.get(callId);
+ CharSequence message;
+ if (disconnectCause.getImsReasonInfo() != null) {
+ message = diagnosticCall.onCallDisconnected(disconnectCause.getImsReasonInfo());
+ } else {
+ message = diagnosticCall.onCallDisconnected(
+ disconnectCause.getTelephonyDisconnectCause(),
+ disconnectCause.getTelephonyPreciseDisconnectCause());
+ }
+ try {
+ mAdapter.overrideDisconnectMessage(callId, message);
+ } catch (RemoteException e) {
+ Log.w(this, "handleCallDisconnected: call=%s; cause=%s; %s",
+ callId, disconnectCause, e);
+ }
+ }
+
+ /**
* Handles an incoming bluetooth call quality report from Telecom. Notifies via
* {@link CallDiagnosticService#onBluetoothCallQualityReportReceived(
* BluetoothCallQualityReport)}.
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 1472a4a..ed7b79f 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -16,9 +16,13 @@
package android.telecom;
+import android.annotation.Nullable;
import android.media.ToneGenerator;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.Annotation;
+import android.telephony.PreciseDisconnectCause;
+import android.telephony.ims.ImsReasonInfo;
import android.text.TextUtils;
import java.util.Objects;
@@ -112,6 +116,9 @@
private CharSequence mDisconnectDescription;
private String mDisconnectReason;
private int mToneToPlay;
+ private int mTelephonyDisconnectCause;
+ private int mTelephonyPreciseDisconnectCause;
+ private ImsReasonInfo mImsReasonInfo;
/**
* Creates a new DisconnectCause.
@@ -155,11 +162,36 @@
*/
public DisconnectCause(int code, CharSequence label, CharSequence description, String reason,
int toneToPlay) {
+ this(code, label, description, reason, toneToPlay,
+ android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
+ PreciseDisconnectCause.ERROR_UNSPECIFIED,
+ null /* imsReasonInfo */);
+ }
+
+ /**
+ * Creates a new DisconnectCause instance.
+ * @param code The code for the disconnect cause.
+ * @param label The localized label to show to the user to explain the disconnect.
+ * @param description The localized description to show to the user to explain the disconnect.
+ * @param reason The reason for the disconnect.
+ * @param toneToPlay The tone to play on disconnect, as defined in {@link ToneGenerator}.
+ * @param telephonyDisconnectCause The Telephony disconnect cause.
+ * @param telephonyPreciseDisconnectCause The Telephony precise disconnect cause.
+ * @param imsReasonInfo The relevant {@link ImsReasonInfo}, or {@code null} if not available.
+ * @hide
+ */
+ public DisconnectCause(int code, CharSequence label, CharSequence description, String reason,
+ int toneToPlay, @Annotation.DisconnectCauses int telephonyDisconnectCause,
+ @Annotation.PreciseDisconnectCauses int telephonyPreciseDisconnectCause,
+ @Nullable ImsReasonInfo imsReasonInfo) {
mDisconnectCode = code;
mDisconnectLabel = label;
mDisconnectDescription = description;
mDisconnectReason = reason;
mToneToPlay = toneToPlay;
+ mTelephonyDisconnectCause = telephonyDisconnectCause;
+ mTelephonyPreciseDisconnectCause = telephonyPreciseDisconnectCause;
+ mImsReasonInfo = imsReasonInfo;
}
/**
@@ -209,6 +241,33 @@
}
/**
+ * Returns the telephony {@link android.telephony.DisconnectCause} for the call.
+ * @return The disconnect cause.
+ * @hide
+ */
+ public @Annotation.DisconnectCauses int getTelephonyDisconnectCause() {
+ return mTelephonyDisconnectCause;
+ }
+
+ /**
+ * Returns the telephony {@link android.telephony.PreciseDisconnectCause} for the call.
+ * @return The precise disconnect cause.
+ * @hide
+ */
+ public @Annotation.PreciseDisconnectCauses int getTelephonyPreciseDisconnectCause() {
+ return mTelephonyPreciseDisconnectCause;
+ }
+
+ /**
+ * Returns the telephony {@link ImsReasonInfo} associated with the call disconnection.
+ * @return The {@link ImsReasonInfo} or {@code null} if not known.
+ * @hide
+ */
+ public @Nullable ImsReasonInfo getImsReasonInfo() {
+ return mImsReasonInfo;
+ }
+
+ /**
* Returns the tone to play when disconnected.
*
* @return the tone as defined in {@link ToneGenerator} to play when disconnected.
@@ -217,7 +276,8 @@
return mToneToPlay;
}
- public static final @android.annotation.NonNull Creator<DisconnectCause> CREATOR = new Creator<DisconnectCause>() {
+ public static final @android.annotation.NonNull Creator<DisconnectCause> CREATOR
+ = new Creator<DisconnectCause>() {
@Override
public DisconnectCause createFromParcel(Parcel source) {
int code = source.readInt();
@@ -225,7 +285,11 @@
CharSequence description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
String reason = source.readString();
int tone = source.readInt();
- return new DisconnectCause(code, label, description, reason, tone);
+ int telephonyDisconnectCause = source.readInt();
+ int telephonyPreciseDisconnectCause = source.readInt();
+ ImsReasonInfo imsReasonInfo = source.readParcelable(null);
+ return new DisconnectCause(code, label, description, reason, tone,
+ telephonyDisconnectCause, telephonyPreciseDisconnectCause, imsReasonInfo);
}
@Override
@@ -241,6 +305,9 @@
TextUtils.writeToParcel(mDisconnectDescription, destination, flags);
destination.writeString(mDisconnectReason);
destination.writeInt(mToneToPlay);
+ destination.writeInt(mTelephonyDisconnectCause);
+ destination.writeInt(mTelephonyPreciseDisconnectCause);
+ destination.writeParcelable(mImsReasonInfo, 0);
}
@Override
@@ -254,7 +321,10 @@
+ Objects.hashCode(mDisconnectLabel)
+ Objects.hashCode(mDisconnectDescription)
+ Objects.hashCode(mDisconnectReason)
- + Objects.hashCode(mToneToPlay);
+ + Objects.hashCode(mToneToPlay)
+ + Objects.hashCode(mTelephonyDisconnectCause)
+ + Objects.hashCode(mTelephonyPreciseDisconnectCause)
+ + Objects.hashCode(mImsReasonInfo);
}
@Override
@@ -265,7 +335,11 @@
&& Objects.equals(mDisconnectLabel, d.getLabel())
&& Objects.equals(mDisconnectDescription, d.getDescription())
&& Objects.equals(mDisconnectReason, d.getReason())
- && Objects.equals(mToneToPlay, d.getTone());
+ && Objects.equals(mToneToPlay, d.getTone())
+ && Objects.equals(mTelephonyDisconnectCause, d.getTelephonyDisconnectCause())
+ && Objects.equals(mTelephonyPreciseDisconnectCause,
+ d.getTelephonyPreciseDisconnectCause())
+ && Objects.equals(mImsReasonInfo, d.getImsReasonInfo());
}
return false;
}
@@ -325,6 +399,11 @@
+ " Label: (" + label + ")"
+ " Description: (" + description + ")"
+ " Reason: (" + reason + ")"
- + " Tone: (" + mToneToPlay + ") ]";
+ + " Tone: (" + mToneToPlay + ") "
+ + " TelephonyCause: " + mTelephonyDisconnectCause + "/"
+ + mTelephonyPreciseDisconnectCause
+ + " ImsReasonInfo: "
+ + mImsReasonInfo
+ + "]";
}
}
diff --git a/telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl b/telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl
index 65b4d19..fc9879a 100644
--- a/telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl
@@ -18,6 +18,7 @@
import android.telecom.BluetoothCallQualityReport;
import android.telecom.CallAudioState;
+import android.telecom.DisconnectCause;
import android.telecom.ParcelableCall;
import com.android.internal.telecom.ICallDiagnosticServiceAdapter;
@@ -34,4 +35,5 @@
void removeDiagnosticCall(in String callId);
void receiveDeviceToDeviceMessage(in String callId, int message, int value);
void receiveBluetoothCallQualityReport(in BluetoothCallQualityReport qualityReport);
+ void notifyCallDisconnected(in String callId, in DisconnectCause disconnectCause);
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 4dc6c7c..3084b14 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -14634,6 +14634,10 @@
/**
* Enable/Disable E-UTRA-NR Dual Connectivity.
*
+ * This api is supported only if
+ * {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
+ * ({@link TelephonyManager#CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE})
+ * returns true.
* @param nrDualConnectivityState expected NR dual connectivity state
* This can be passed following states
* <ol>
@@ -14648,6 +14652,9 @@
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(
+ enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+ value = TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE)
public @EnableNrDualConnectivityResult int setNrDualConnectivityState(
@NrDualConnectivityState int nrDualConnectivityState) {
try {
@@ -14667,15 +14674,21 @@
/**
* Is E-UTRA-NR Dual Connectivity enabled.
+ * This api is supported only if
+ * {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
+ * ({@link TelephonyManager#CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE})
+ * returns true.
* @return true if dual connectivity is enabled else false. Enabled state does not mean dual
* connectivity is active. It means the device is allowed to connect to both primary and
* secondary cell.
- * <p>Requires Permission:
- * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
* @throws IllegalStateException if the Telephony process is not currently available.
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(
+ enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+ value = TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE)
public boolean isNrDualConnectivityEnabled() {
try {
ITelephony telephony = getITelephony();
@@ -14927,11 +14940,23 @@
public static final String CAPABILITY_ALLOWED_NETWORK_TYPES_USED =
"CAPABILITY_ALLOWED_NETWORK_TYPES_USED";
+ /**
+ * Indicates whether {@link #setNrDualConnectivityState()} and
+ * {@link #isNrDualConnectivityEnabled()} ()} are available. See comments
+ * on respective methods for more information.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE =
+ "CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE";
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@StringDef(prefix = "CAPABILITY_", value = {
CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE,
CAPABILITY_ALLOWED_NETWORK_TYPES_USED,
+ CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE
})
public @interface RadioInterfaceCapability {}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index c92d40c..3c12aaa 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.close
+import android.view.WindowManagerPolicyConstants
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -49,7 +50,12 @@
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 5)
+ .getConfigNonRotationTests(
+ repetitions = 5,
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ )
+ )
}
}
}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index 1f880f6..8359ccf 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.close
+import android.view.WindowManagerPolicyConstants
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -49,7 +50,12 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 5)
+ .getConfigNonRotationTests(
+ repetitions = 5,
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ )
+ )
}
}
}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
index f7e7493..fad25b4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
@@ -16,31 +16,13 @@
package com.android.server.wm.flicker.helpers
-import android.os.RemoteException
-import android.view.Surface
import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.rules.ChangeDisplayOrientationRule
/**
* Changes the device [rotation] and wait for the rotation animation to complete
*
* @param rotation New device rotation
*/
-fun Flicker.setRotation(rotation: Int) {
- try {
- when (rotation) {
- Surface.ROTATION_270 -> device.setOrientationRight()
- Surface.ROTATION_90 -> device.setOrientationLeft()
- Surface.ROTATION_0 -> device.setOrientationNatural()
- else -> device.setOrientationNatural()
- }
-
- wmHelper.waitForRotation(rotation)
- wmHelper.waitForNavBarStatusBarVisible()
- wmHelper.waitForAppTransitionIdle()
-
- // Ensure WindowManagerService wait until all animations have completed
- instrumentation.uiAutomation.syncInputTransactions()
- } catch (e: RemoteException) {
- throw RuntimeException(e)
- }
-}
\ No newline at end of file
+fun Flicker.setRotation(rotation: Int) =
+ ChangeDisplayOrientationRule.setRotation(rotation, instrumentation, wmHelper)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index 47eaddf..0dcc8c9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -20,6 +20,7 @@
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import android.view.WindowManagerPolicyConstants
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -164,7 +165,12 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 5)
+ .getConfigNonRotationTests(
+ repetitions = 5,
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ )
+ )
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index 26afb79..7a1bb11 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -19,6 +19,7 @@
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import android.view.WindowManagerPolicyConstants
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -178,7 +179,12 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 5)
+ .getConfigNonRotationTests(
+ repetitions = 5,
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ )
+ )
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 2c4c627..2d09d23 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -19,6 +19,7 @@
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import android.view.WindowManagerPolicyConstants
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -141,7 +142,12 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 5)
+ .getConfigNonRotationTests(
+ repetitions = 5,
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ )
+ )
}
}
}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 2bcdcd9..73760c5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -19,6 +19,7 @@
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import android.view.WindowManagerPolicyConstants
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -162,8 +163,13 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 5,
- supportedRotations = listOf(Surface.ROTATION_0))
+ .getConfigNonRotationTests(
+ repetitions = 5,
+ supportedRotations = listOf(Surface.ROTATION_0),
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ )
+ )
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index 6b2b930..2815e05 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -19,6 +19,7 @@
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import android.view.WindowManagerPolicyConstants
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -168,8 +169,13 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 5,
- supportedRotations = listOf(Surface.ROTATION_0))
+ .getConfigNonRotationTests(
+ repetitions = 5,
+ supportedRotations = listOf(Surface.ROTATION_0),
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ )
+ )
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index 0cd5d79..7ca985e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -19,6 +19,7 @@
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import android.view.WindowManagerPolicyConstants
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -184,7 +185,12 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 1)
+ .getConfigNonRotationTests(
+ repetitions = 1,
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ )
+ )
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 56ed21b..417a5c5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.launch
+import android.view.WindowManagerPolicyConstants
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -60,7 +61,12 @@
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests()
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ )
+ )
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 4a32a9e..fee01d2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -17,6 +17,7 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Presubmit
+import android.view.WindowManagerPolicyConstants
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -136,7 +137,12 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 5)
+ .getConfigNonRotationTests(
+ repetitions = 5,
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ )
+ )
}
}
}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index a8b5ea1..bdae810 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.launch
+import android.view.WindowManagerPolicyConstants
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -72,7 +73,12 @@
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests()
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ )
+ )
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 6985b36..80b2237 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -17,6 +17,7 @@
package com.android.server.wm.flicker.rotation
import android.platform.test.annotations.Presubmit
+import android.view.WindowManagerPolicyConstants
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -70,7 +71,12 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigRotationTests(repetitions = 5)
+ .getConfigRotationTests(
+ repetitions = 5,
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ )
+ )
}
}
}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 45d3006..dd7103c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -17,6 +17,7 @@
package com.android.server.wm.flicker.rotation
import android.platform.test.annotations.Presubmit
+import android.view.WindowManagerPolicyConstants
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -103,7 +104,12 @@
@JvmStatic
private fun getConfigurations(): List<FlickerTestParameter> {
- return testFactory.getConfigRotationTests(repetitions = 2).flatMap {
+ return testFactory.getConfigRotationTests(
+ repetitions = 2,
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ )
+ ).flatMap {
val defaultRun = it.createConfig(starveUiThread = false)
val busyUiRun = it.createConfig(starveUiThread = true)
listOf(
diff --git a/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
index 2e985fb..b9b347b 100644
--- a/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
+++ b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
@@ -43,7 +43,7 @@
xPrecision, yPrecision, deviceId, edgeFlags, source, displayId)
}
-fun createKeyEvent(action: Int, eventTime: Long): KeyEvent {
+private fun createKeyEvent(action: Int, eventTime: Long): KeyEvent {
val code = KeyEvent.KEYCODE_A
val repeat = 0
return KeyEvent(eventTime, eventTime, action, code, repeat)
diff --git a/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
new file mode 100644
index 0000000..4f95ce5
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.input
+
+import android.os.HandlerThread
+import android.os.Looper
+import android.view.InputChannel
+import android.view.InputEvent
+import android.view.InputEventReceiver
+import android.view.InputEventSender
+import android.view.KeyEvent
+import android.view.MotionEvent
+import java.util.concurrent.CountDownLatch
+import org.junit.Assert.assertEquals
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+
+private fun assertKeyEvent(expected: KeyEvent, received: KeyEvent) {
+ assertEquals(expected.action, received.action)
+ assertEquals(expected.deviceId, received.deviceId)
+ assertEquals(expected.downTime, received.downTime)
+ assertEquals(expected.eventTime, received.eventTime)
+ assertEquals(expected.keyCode, received.keyCode)
+ assertEquals(expected.scanCode, received.scanCode)
+ assertEquals(expected.repeatCount, received.repeatCount)
+ assertEquals(expected.metaState, received.metaState)
+ assertEquals(expected.flags, received.flags)
+ assertEquals(expected.source, received.source)
+ assertEquals(expected.displayId, received.displayId)
+}
+
+class TestInputEventReceiver(channel: InputChannel, looper: Looper) :
+ InputEventReceiver(channel, looper) {
+ companion object {
+ const val TAG = "TestInputEventReceiver"
+ }
+
+ var lastEvent: InputEvent? = null
+
+ override fun onInputEvent(event: InputEvent) {
+ lastEvent = when (event) {
+ is KeyEvent -> KeyEvent.obtain(event)
+ is MotionEvent -> MotionEvent.obtain(event)
+ else -> throw Exception("Received $event is neither a key nor a motion")
+ }
+ finishInputEvent(event, true /*handled*/)
+ }
+}
+
+class TestInputEventSender(channel: InputChannel, looper: Looper) :
+ InputEventSender(channel, looper) {
+ companion object {
+ const val TAG = "TestInputEventSender"
+ }
+ data class FinishedResult(val seq: Int, val handled: Boolean)
+
+ private var mFinishedSignal = CountDownLatch(1)
+ override fun onInputEventFinished(seq: Int, handled: Boolean) {
+ finishedResult = FinishedResult(seq, handled)
+ mFinishedSignal.countDown()
+ }
+ lateinit var finishedResult: FinishedResult
+
+ fun waitForFinish() {
+ mFinishedSignal.await()
+ mFinishedSignal = CountDownLatch(1) // Ready for next event
+ }
+}
+
+class InputEventSenderAndReceiverTest {
+ companion object {
+ private const val TAG = "InputEventSenderAndReceiverTest"
+ }
+ private val mHandlerThread = HandlerThread("Process input events")
+ private lateinit var mReceiver: TestInputEventReceiver
+ private lateinit var mSender: TestInputEventSender
+
+ @Before
+ fun setUp() {
+ val channels = InputChannel.openInputChannelPair("TestChannel")
+ mHandlerThread.start()
+
+ val looper = mHandlerThread.getLooper()
+ mSender = TestInputEventSender(channels[0], looper)
+ mReceiver = TestInputEventReceiver(channels[1], looper)
+ }
+
+ @After
+ fun tearDown() {
+ mHandlerThread.quitSafely()
+ }
+
+ @Test
+ fun testSendAndReceiveKey() {
+ val key = KeyEvent(1 /*downTime*/, 1 /*eventTime*/, KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_A, 0 /*repeat*/)
+ val seq = 10
+ mSender.sendInputEvent(seq, key)
+ mSender.waitForFinish()
+
+ // Check receiver
+ assertKeyEvent(key, mReceiver.lastEvent!! as KeyEvent)
+
+ // Check sender
+ assertEquals(seq, mSender.finishedResult.seq)
+ assertEquals(true, mSender.finishedResult.handled)
+ }
+}
diff --git a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
index e121b68..df58da5 100644
--- a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
+++ b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
@@ -100,7 +100,6 @@
platformCompat.setOverridesForTest(new CompatibilityChangeConfig(mConfig),
packageName);
try {
- uiAutomation.dropShellPermissionIdentity();
mTestStatement.evaluate();
} finally {
adoptShellPermissions(uiAutomation);
diff --git a/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp b/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp
index ce226fd..926ff4d 100644
--- a/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp
+++ b/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp
@@ -130,6 +130,9 @@
return result;
}
sBuffers[slot] = anb;
+ if (timeoutMs == 0) {
+ return android::OK;
+ }
android::sp<android::Fence> fence(new android::Fence(fenceFd));
int waitResult = fence->wait(timeoutMs);
if (waitResult != android::OK) {
@@ -197,6 +200,28 @@
return result;
}
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceSetAsyncMode(JNIEnv* /* env */,
+ jclass /* clazz */,
+ jboolean async) {
+ assert(sAnw);
+ android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+ return surface->setAsyncMode(async);
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceSetDequeueTimeout(
+ JNIEnv* /* env */, jclass /* clazz */, jlong timeoutMs) {
+ assert(sAnw);
+ android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+ return surface->setDequeueTimeout(timeoutMs);
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceSetMaxDequeuedBufferCount(
+ JNIEnv* /* env */, jclass /* clazz */, jint maxDequeuedBuffers) {
+ assert(sAnw);
+ android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+ return surface->setMaxDequeuedBufferCount(maxDequeuedBuffers);
+}
+
JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_NativeWindowSetBufferCount(
JNIEnv* /* env */, jclass /* clazz */, jint count) {
assert(sAnw);
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt
index 7d278dc..b67dc380 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt
@@ -17,6 +17,7 @@
import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@@ -93,4 +94,80 @@
assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames)
}
+
+ @Test
+ // Leave IGBP in sync mode, try to dequeue and queue as fast as possible. Check that we
+ // occasionally get timeout errors.
+ fun testSyncMode_dequeueWithoutBlockingFails() {
+ val numFrames = 1000L
+ runOnUiThread { activity ->
+ assertEquals(0, activity.mSurfaceProxy.SurfaceSetDequeueTimeout(3L))
+ var failures = false
+ for (i in 1..numFrames) {
+ if (activity.mSurfaceProxy.SurfaceDequeueBuffer(0, 0 /* ms */) != 0) {
+ failures = true
+ break
+ }
+ activity.mSurfaceProxy.SurfaceQueueBuffer(0)
+ }
+ assertTrue(failures)
+ }
+ }
+
+ @Test
+ // Set IGBP to be in async mode, try to dequeue and queue as fast as possible. Client should be
+ // able to dequeue and queue buffers without being blocked.
+ fun testAsyncMode_dequeueWithoutBlocking() {
+ val numFrames = 1000L
+ runOnUiThread { activity ->
+ assertEquals(0, activity.mSurfaceProxy.SurfaceSetDequeueTimeout(3L))
+ assertEquals(0, activity.mSurfaceProxy.SurfaceSetAsyncMode(async = true))
+ for (i in 1..numFrames) {
+ assertEquals(0, activity.mSurfaceProxy.SurfaceDequeueBuffer(0, 0 /* ms */))
+ activity.mSurfaceProxy.SurfaceQueueBuffer(0)
+ }
+ }
+ }
+
+ @Test
+ // Disable triple buffering in the system and leave IGBP in sync mode. Check that we
+ // occasionally get timeout errors.
+ fun testSyncModeWithDisabledTripleBuffering_dequeueWithoutBlockingFails() {
+ val numFrames = 1000L
+ runOnUiThread { activity ->
+ assertEquals(0, activity.mSurfaceProxy.SurfaceSetMaxDequeuedBufferCount(1))
+ assertEquals(0, activity.mSurfaceProxy.SurfaceSetDequeueTimeout(3L))
+ var failures = false
+ for (i in 1..numFrames) {
+ if (activity.mSurfaceProxy.SurfaceDequeueBuffer(0, 0 /* ms */) != 0) {
+ failures = true
+ break
+ }
+ activity.mSurfaceProxy.SurfaceQueueBuffer(0)
+ }
+ assertTrue(failures)
+ }
+ }
+
+ @Test
+ // Disable triple buffering in the system and set IGBP to be in async mode. Try to dequeue and
+ // queue as fast as possible. Without triple buffering, the client does not have an extra buffer
+ // to dequeue and will not be able to dequeue and queue buffers without being blocked.
+ fun testAsyncModeWithDisabledTripleBuffering_dequeueWithoutBlockingFails() {
+ val numFrames = 1000L
+ runOnUiThread { activity ->
+ assertEquals(0, activity.mSurfaceProxy.SurfaceSetMaxDequeuedBufferCount(1))
+ assertEquals(0, activity.mSurfaceProxy.SurfaceSetDequeueTimeout(3L))
+ assertEquals(0, activity.mSurfaceProxy.SurfaceSetAsyncMode(async = true))
+ var failures = false
+ for (i in 1..numFrames) {
+ if (activity.mSurfaceProxy.SurfaceDequeueBuffer(0, 0 /* ms */) != 0) {
+ failures = true
+ break
+ }
+ activity.mSurfaceProxy.SurfaceQueueBuffer(0)
+ }
+ assertTrue(failures)
+ }
+ }
}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt
index cfbd3ac..45a7094 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt
@@ -54,7 +54,13 @@
external fun SurfaceSetScalingMode(scalingMode: Int)
external fun SurfaceDequeueBuffer(slot: Int, timeoutMs: Int): Int
external fun SurfaceCancelBuffer(slot: Int)
- external fun SurfaceQueueBuffer(slot: Int, freeSlot: Boolean = true)
+ external fun SurfaceQueueBuffer(slot: Int, freeSlot: Boolean = true): Int
+ external fun SurfaceSetAsyncMode(async: Boolean): Int
+ external fun SurfaceSetDequeueTimeout(timeout: Long): Int
+ external fun SurfaceQuery(what: Int): Int
+ external fun SurfaceSetMaxDequeuedBufferCount(maxDequeuedBuffers: Int): Int
+
+ // system/native_window.h functions
external fun NativeWindowSetBufferCount(count: Int): Int
external fun NativeWindowSetSharedBufferMode(shared: Boolean): Int
external fun NativeWindowSetAutoRefresh(autoRefresh: Boolean): Int
diff --git a/tests/net/common/java/android/net/IpPrefixTest.java b/tests/net/common/java/android/net/IpPrefixTest.java
index 9c0fc7c..50ecb42 100644
--- a/tests/net/common/java/android/net/IpPrefixTest.java
+++ b/tests/net/common/java/android/net/IpPrefixTest.java
@@ -113,6 +113,15 @@
p = new IpPrefix("f00:::/32");
fail("Expected IllegalArgumentException: invalid IPv6 address");
} catch (IllegalArgumentException expected) { }
+
+ p = new IpPrefix("/64");
+ assertEquals("::/64", p.toString());
+
+ p = new IpPrefix("/128");
+ assertEquals("::1/128", p.toString());
+
+ p = new IpPrefix("[2001:db8::123]/64");
+ assertEquals("2001:db8::/64", p.toString());
}
@Test
diff --git a/tests/net/common/java/android/net/LinkAddressTest.java b/tests/net/common/java/android/net/LinkAddressTest.java
index 1eaf30c..2cf3cf9 100644
--- a/tests/net/common/java/android/net/LinkAddressTest.java
+++ b/tests/net/common/java/android/net/LinkAddressTest.java
@@ -53,6 +53,7 @@
import org.junit.runner.RunWith;
import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
@@ -117,6 +118,20 @@
assertEquals(456, address.getScope());
assertTrue(address.isIpv4());
+ address = new LinkAddress("/64", 1 /* flags */, 2 /* scope */);
+ assertEquals(Inet6Address.LOOPBACK, address.getAddress());
+ assertEquals(64, address.getPrefixLength());
+ assertEquals(1, address.getFlags());
+ assertEquals(2, address.getScope());
+ assertTrue(address.isIpv6());
+
+ address = new LinkAddress("[2001:db8::123]/64", 3 /* flags */, 4 /* scope */);
+ assertEquals(InetAddresses.parseNumericAddress("2001:db8::123"), address.getAddress());
+ assertEquals(64, address.getPrefixLength());
+ assertEquals(3, address.getFlags());
+ assertEquals(4, address.getScope());
+ assertTrue(address.isIpv6());
+
// InterfaceAddress doesn't have a constructor. Fetch some from an interface.
List<InterfaceAddress> addrs = NetworkInterface.getByName("lo").getInterfaceAddresses();
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index a07bce3..8932892 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -30,12 +30,21 @@
* -e selectTest_verbose true \
* com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
* </pre>
+ *
+ * <p>Use this filter when running FrameworksMockingCoreTests as
+ * <pre>
+ * adb shell am instrument -w \
+ * -e filter com.android.server.wm.test.filters.FrameworksTestsFilter \
+ * -e selectTest_verbose true \
+ * com.android.frameworks.mockingcoretests/androidx.test.runner.AndroidJUnitRunner
+ * </pre>
*/
public final class FrameworksTestsFilter extends SelectTest {
private static final String[] SELECTED_TESTS = {
// Test specifications for FrameworksMockingCoreTests.
"android.app.activity.ActivityThreadClientTest",
+ "android.view.DisplayTest",
// Test specifications for FrameworksCoreTests.
"android.app.servertransaction.", // all tests under the package.
"android.view.CutoutSpecificationTest",