Merge "Fixing gradle complain on skipping constructor method mid-way" into sc-dev
diff --git a/Android.bp b/Android.bp
index dc92586..202e548 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1295,6 +1295,18 @@
],
}
+python_binary_host {
+ name: "update_font_metadata",
+ defaults: ["base_default"],
+ main: "tools/fonts/update_font_metadata.py",
+ srcs: [
+ "tools/fonts/update_font_metadata.py",
+ ],
+ libs: [
+ "fontTools",
+ ],
+}
+
filegroup {
name: "framework-media-annotation-srcs",
srcs: [
diff --git a/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt b/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt
index 9e519f7..1d94d7e 100644
--- a/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt
+++ b/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt
@@ -178,7 +178,7 @@
// For testing, just disable enforcement to avoid hooking up to compat framework
ParseTypeImpl(ParseInput.Callback { _, _, _ -> false })
}
- val parser = ParsingPackageUtils(false, null, null,
+ val parser = ParsingPackageUtils(false, null, null, emptyList(),
object : ParsingPackageUtils.Callback {
override fun hasFeature(feature: String) = true
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
index b1394c1..a3b8013 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -143,11 +143,11 @@
method public void close();
method public void getByUri(@NonNull android.app.appsearch.GetByUriRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,android.app.appsearch.GenericDocument>);
method public void getSchema(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.util.Set<android.app.appsearch.AppSearchSchema>>>);
- method public void putDocuments(@NonNull android.app.appsearch.PutDocumentsRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,java.lang.Void>);
- method @NonNull public android.app.appsearch.SearchResults query(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor);
- method public void removeByQuery(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
- method public void removeByUri(@NonNull android.app.appsearch.RemoveByUriRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,java.lang.Void>);
+ method public void put(@NonNull android.app.appsearch.PutDocumentsRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,java.lang.Void>);
+ method public void remove(@NonNull android.app.appsearch.RemoveByUriRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,java.lang.Void>);
+ method public void remove(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
method @NonNull public void reportUsage(@NonNull android.app.appsearch.ReportUsageRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
+ method @NonNull public android.app.appsearch.SearchResults search(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor);
method public void setSchema(@NonNull android.app.appsearch.SetSchemaRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.SetSchemaResponse>>);
}
@@ -216,7 +216,7 @@
public class GlobalSearchSession implements java.io.Closeable {
method public void close();
- method @NonNull public android.app.appsearch.SearchResults query(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor);
+ method @NonNull public android.app.appsearch.SearchResults search(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor);
}
public class PackageIdentifier {
@@ -226,13 +226,13 @@
}
public final class PutDocumentsRequest {
- method @NonNull public java.util.List<android.app.appsearch.GenericDocument> getDocuments();
+ method @NonNull public java.util.List<android.app.appsearch.GenericDocument> getGenericDocuments();
}
public static final class PutDocumentsRequest.Builder {
ctor public PutDocumentsRequest.Builder();
- method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocument(@NonNull android.app.appsearch.GenericDocument...);
- method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocument(@NonNull java.util.Collection<? extends android.app.appsearch.GenericDocument>);
+ method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocuments(@NonNull android.app.appsearch.GenericDocument...);
+ method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocuments(@NonNull java.util.Collection<? extends android.app.appsearch.GenericDocument>);
method @NonNull public android.app.appsearch.PutDocumentsRequest build();
}
@@ -290,14 +290,14 @@
}
public final class SearchSpec {
+ method @NonNull public java.util.List<java.lang.String> getFilterNamespaces();
method @NonNull public java.util.List<java.lang.String> getFilterPackageNames();
+ method @NonNull public java.util.List<java.lang.String> getFilterSchemas();
method public int getMaxSnippetSize();
- method @NonNull public java.util.List<java.lang.String> getNamespaces();
method public int getOrder();
method @NonNull public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getProjections();
method public int getRankingStrategy();
method public int getResultCountPerPage();
- method @NonNull public java.util.List<java.lang.String> getSchemaTypes();
method public int getSnippetCount();
method public int getSnippetCountPerProperty();
method public int getTermMatch();
@@ -316,14 +316,14 @@
public static final class SearchSpec.Builder {
ctor public SearchSpec.Builder();
+ method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterNamespaces(@NonNull java.lang.String...);
+ method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterNamespaces(@NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterPackageNames(@NonNull java.lang.String...);
method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterPackageNames(@NonNull java.util.Collection<java.lang.String>);
- method @NonNull public android.app.appsearch.SearchSpec.Builder addNamespace(@NonNull java.lang.String...);
- method @NonNull public android.app.appsearch.SearchSpec.Builder addNamespace(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterSchemas(@NonNull java.lang.String...);
+ method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterSchemas(@NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.SearchSpec.Builder addProjection(@NonNull String, @NonNull java.lang.String...);
method @NonNull public android.app.appsearch.SearchSpec.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>);
- method @NonNull public android.app.appsearch.SearchSpec.Builder addSchemaType(@NonNull java.lang.String...);
- method @NonNull public android.app.appsearch.SearchSpec.Builder addSchemaType(@NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.SearchSpec build();
method @NonNull public android.app.appsearch.SearchSpec.Builder setMaxSnippetSize(@IntRange(from=0, to=android.app.appsearch.SearchSpec.MAX_SNIPPET_SIZE_LIMIT) int);
method @NonNull public android.app.appsearch.SearchSpec.Builder setOrder(int);
@@ -344,8 +344,8 @@
public static final class SetSchemaRequest.Builder {
ctor public SetSchemaRequest.Builder();
- method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchema(@NonNull android.app.appsearch.AppSearchSchema...);
- method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchema(@NonNull java.util.Collection<android.app.appsearch.AppSearchSchema>);
+ method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchemas(@NonNull android.app.appsearch.AppSearchSchema...);
+ method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchemas(@NonNull java.util.Collection<android.app.appsearch.AppSearchSchema>);
method @NonNull public android.app.appsearch.SetSchemaRequest build();
method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setForceOverride(boolean);
method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setMigrator(@NonNull String, @NonNull android.app.appsearch.AppSearchSchema.Migrator);
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index 814800e..69d4e53 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -270,12 +270,12 @@
* they were successfully indexed, or a failed {@link AppSearchResult} otherwise.
* @throws RuntimeException If an error occurred during the execution.
* @hide
- * @deprecated use {@link AppSearchSession#putDocuments} instead.
+ * @deprecated use {@link AppSearchSession#put} instead.
*/
public AppSearchBatchResult<String, Void> putDocuments(@NonNull PutDocumentsRequest request) {
// TODO(b/146386470): Transmit these documents as a RemoteStream instead of sending them in
// one big list.
- List<GenericDocument> documents = request.getDocuments();
+ List<GenericDocument> documents = request.getGenericDocuments();
List<Bundle> documentBundles = new ArrayList<>(documents.size());
for (int i = 0; i < documents.size(); i++) {
documentBundles.add(documents.get(i).getBundle());
@@ -330,7 +330,7 @@
DEFAULT_DATABASE_NAME,
request.getNamespace(),
uris,
- request.getProjectionsVisibleToPackagesInternal(),
+ request.getProjectionsInternal(),
mContext.getUserId(),
new IAppSearchBatchResultCallback.Stub() {
public void onResult(AppSearchBatchResult result) {
@@ -465,7 +465,7 @@
* {@link AppSearchResult} with a result code of {@link AppSearchResult#RESULT_NOT_FOUND}.
* @throws RuntimeException If an error occurred during the execution.
* @hide
- * @deprecated use {@link AppSearchSession#removeByUri} instead.
+ * @deprecated use {@link AppSearchSession#remove} instead.
*/
public AppSearchBatchResult<String, Void> removeByUri(@NonNull RemoveByUriRequest request) {
List<String> uris = new ArrayList<>(request.getUris());
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index 670f8b9..8723515 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -102,7 +102,7 @@
}
/**
- * Sets the schema that will be used by documents provided to the {@link #putDocuments} method.
+ * Sets the schema that will be used by documents provided to the {@link #put} method.
*
* <p>The schema provided here is compared to the stored copy of the schema previously supplied
* to {@link #setSchema}, if any, to determine how to treat existing documents. The following
@@ -268,7 +268,7 @@
* {@link Throwable} if an unexpected internal error occurred in AppSearch
* service.
*/
- public void putDocuments(
+ public void put(
@NonNull PutDocumentsRequest request,
@NonNull @CallbackExecutor Executor executor,
@NonNull BatchResultCallback<String, Void> callback) {
@@ -276,7 +276,7 @@
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
- List<GenericDocument> documents = request.getDocuments();
+ List<GenericDocument> documents = request.getGenericDocuments();
List<Bundle> documentBundles = new ArrayList<>(documents.size());
for (int i = 0; i < documents.size(); i++) {
documentBundles.add(documents.get(i).getBundle());
@@ -327,7 +327,7 @@
mDatabaseName,
request.getNamespace(),
new ArrayList<>(request.getUris()),
- request.getProjectionsVisibleToPackagesInternal(),
+ request.getProjectionsInternal(),
mUserId,
new IAppSearchBatchResultCallback.Stub() {
public void onResult(AppSearchBatchResult result) {
@@ -423,7 +423,7 @@
* @return The search result of performing this operation.
*/
@NonNull
- public SearchResults query(
+ public SearchResults search(
@NonNull String queryExpression,
@NonNull SearchSpec searchSpec,
@NonNull @CallbackExecutor Executor executor) {
@@ -441,7 +441,7 @@
* <p>A usage report represents an event in which a user interacted with or viewed a document.
*
* <p>For each call to {@link #reportUsage}, AppSearch updates usage count and usage recency
- * metrics for that particular document. These metrics are used for ordering {@link #query}
+ * metrics for that particular document. These metrics are used for ordering {@link #search}
* results by the {@link SearchSpec#RANKING_STRATEGY_USAGE_COUNT} and
* {@link SearchSpec#RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP} ranking strategies.
*
@@ -494,7 +494,7 @@
* {@link Throwable} if an unexpected internal error occurred in AppSearch
* service.
*/
- public void removeByUri(
+ public void remove(
@NonNull RemoveByUriRequest request,
@NonNull @CallbackExecutor Executor executor,
@NonNull BatchResultCallback<String, Void> callback) {
@@ -523,7 +523,8 @@
/**
* Removes {@link GenericDocument}s from the index by Query. Documents will be removed if they
* match the {@code queryExpression} in given namespaces and schemaTypes which is set via
- * {@link SearchSpec.Builder#addNamespace} and {@link SearchSpec.Builder#addSchemaType}.
+ * {@link SearchSpec.Builder#addFilterNamespaces} and
+ * {@link SearchSpec.Builder#addFilterSchemas}.
*
* <p> An empty {@code queryExpression} matches all documents.
*
@@ -539,7 +540,8 @@
* the operation succeeds, the callback will be invoked with
* {@code null}.
*/
- public void removeByQuery(@NonNull String queryExpression,
+ public void remove(
+ @NonNull String queryExpression,
@NonNull SearchSpec searchSpec,
@NonNull @CallbackExecutor Executor executor,
@NonNull Consumer<AppSearchResult<Void>> callback) {
diff --git a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
index 6bb8554..8651834 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
@@ -134,7 +134,7 @@
* @return The search result of performing this operation.
*/
@NonNull
- public SearchResults query(
+ public SearchResults search(
@NonNull String queryExpression,
@NonNull SearchSpec searchSpec,
@NonNull @CallbackExecutor Executor executor) {
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 2e00ff2..e94b3b2 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
@@ -141,10 +141,6 @@
}
/** Adds a property to the given type. */
- // TODO(b/171360120): MissingGetterMatchingBuilder expects a method called getPropertys, but
- // we provide the (correct) method getProperties. Once the bug referenced in this TODO is
- // fixed, remove this SuppressLint.
- @SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public AppSearchSchema.Builder addProperty(@NonNull PropertyConfig propertyConfig) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
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 2f02808..4c11514 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
@@ -22,6 +22,7 @@
import android.annotation.SuppressLint;
import android.app.appsearch.util.BundleUtil;
import android.os.Bundle;
+import android.os.Parcelable;
import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -37,9 +38,9 @@
*
* <p>Documents are constructed via {@link GenericDocument.Builder}.
*
- * @see AppSearchSession#putDocuments
+ * @see AppSearchSession#put
* @see AppSearchSession#getByUri
- * @see AppSearchSession#query
+ * @see AppSearchSession#search
*/
public class GenericDocument {
private static final String TAG = "AppSearchGenericDocumen";
@@ -210,7 +211,7 @@
Object property = mProperties.get(key);
if (property instanceof ArrayList) {
return getPropertyBytesArray(key);
- } else if (property instanceof Bundle[]) {
+ } else if (property instanceof Parcelable[]) {
return getPropertyDocumentArray(key);
}
return property;
@@ -436,7 +437,7 @@
@Nullable
public GenericDocument[] getPropertyDocumentArray(@NonNull String key) {
Preconditions.checkNotNull(key);
- Bundle[] bundles = getAndCastPropertyArray(key, Bundle[].class);
+ Parcelable[] bundles = getAndCastPropertyArray(key, Parcelable[].class);
if (bundles == null || bundles.length == 0) {
return null;
}
@@ -446,7 +447,18 @@
Log.e(TAG, "The inner bundle is null at " + i + ", for key: " + key);
continue;
}
- documents[i] = new GenericDocument(bundles[i]);
+ if (!(bundles[i] instanceof Bundle)) {
+ Log.e(
+ TAG,
+ "The inner element at "
+ + i
+ + " is a "
+ + bundles[i].getClass()
+ + ", not a Bundle for key: "
+ + key);
+ continue;
+ }
+ documents[i] = new GenericDocument((Bundle) bundles[i]);
}
return documents;
}
@@ -574,8 +586,8 @@
* @param schemaType The schema type of the {@link GenericDocument}. The passed-in {@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#putDocuments}. Otherwise, the document will be rejected by {@link
- * AppSearchSession#putDocuments}.
+ * AppSearchSession#put}. Otherwise, the document will be rejected by {@link
+ * AppSearchSession#put}.
*/
@SuppressWarnings("unchecked")
public Builder(@NonNull String uri, @NonNull String schemaType) {
@@ -817,7 +829,7 @@
private void putInPropertyBundle(@NonNull String key, @NonNull GenericDocument[] values) {
validateRepeatedPropertyLength(key, values.length);
- Bundle[] documentBundles = new Bundle[values.length];
+ Parcelable[] documentBundles = new Parcelable[values.length];
for (int i = 0; i < values.length; i++) {
if (values[i] == null) {
throw new IllegalArgumentException("The document at " + i + " is null.");
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 38e0046..0fcf061 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
@@ -96,7 +96,7 @@
* @hide
*/
@NonNull
- public Map<String, List<String>> getProjectionsVisibleToPackagesInternal() {
+ public Map<String, List<String>> getProjectionsInternal() {
return mTypePropertyPathsMap;
}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java
index 0f141d6..05b2128 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java
@@ -16,8 +16,8 @@
package android.app.appsearch;
+
import android.annotation.NonNull;
-import android.annotation.SuppressLint;
import com.android.internal.util.Preconditions;
@@ -41,7 +41,7 @@
/** Returns the documents that are part of this request. */
@NonNull
- public List<GenericDocument> getDocuments() {
+ public List<GenericDocument> getGenericDocuments() {
return Collections.unmodifiableList(mDocuments);
}
@@ -55,17 +55,15 @@
private boolean mBuilt = false;
/** Adds one or more {@link GenericDocument} objects to the request. */
- @SuppressLint("MissingGetterMatchingBuilder") // Merged list available from getDocuments()
@NonNull
- public Builder addGenericDocument(@NonNull GenericDocument... documents) {
+ public Builder addGenericDocuments(@NonNull GenericDocument... documents) {
Preconditions.checkNotNull(documents);
- return addGenericDocument(Arrays.asList(documents));
+ return addGenericDocuments(Arrays.asList(documents));
}
/** Adds a collection of {@link GenericDocument} objects to the request. */
- @SuppressLint("MissingGetterMatchingBuilder") // Merged list available from getDocuments()
@NonNull
- public Builder addGenericDocument(
+ public Builder addGenericDocuments(
@NonNull Collection<? extends GenericDocument> documents) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(documents);
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 a04da34..2104198d 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
@@ -29,7 +29,7 @@
/**
* Encapsulates a request to remove documents by namespace and URI.
*
- * @see AppSearchSession#removeByUri
+ * @see AppSearchSession#remove
*/
public final class RemoveByUriRequest {
private final String mNamespace;
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
index 963062c..f72f8e1 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
@@ -19,7 +19,6 @@
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
-import android.annotation.SuppressLint;
import android.app.appsearch.exceptions.IllegalSearchSpecException;
import android.os.Bundle;
import android.util.ArrayMap;
@@ -49,7 +48,7 @@
public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
static final String TERM_MATCH_TYPE_FIELD = "termMatchType";
- static final String SCHEMA_TYPE_FIELD = "schemaType";
+ static final String SCHEMA_FIELD = "schema";
static final String NAMESPACE_FIELD = "namespace";
static final String PACKAGE_NAME_FIELD = "packageName";
static final String NUM_PER_PAGE_FIELD = "numPerPage";
@@ -171,12 +170,12 @@
* <p>If empty, the query will search over all schema types.
*/
@NonNull
- public List<String> getSchemaTypes() {
- List<String> schemaTypes = mBundle.getStringArrayList(SCHEMA_TYPE_FIELD);
- if (schemaTypes == null) {
+ public List<String> getFilterSchemas() {
+ List<String> schemas = mBundle.getStringArrayList(SCHEMA_FIELD);
+ if (schemas == null) {
return Collections.emptyList();
}
- return Collections.unmodifiableList(schemaTypes);
+ return Collections.unmodifiableList(schemas);
}
/**
@@ -185,7 +184,7 @@
* <p>If empty, the query will search over all namespaces.
*/
@NonNull
- public List<String> getNamespaces() {
+ public List<String> getFilterNamespaces() {
List<String> namespaces = mBundle.getStringArrayList(NAMESPACE_FIELD);
if (namespaces == null) {
return Collections.emptyList();
@@ -209,24 +208,6 @@
return Collections.unmodifiableList(packageNames);
}
- /**
- * Returns the list of package names to search over.
- *
- * <p>If unset, the query will search over all packages that the caller has access to. If
- * package names are specified which caller doesn't have access to, then those package names
- * will be ignored.
- *
- * @hide
- */
- @NonNull
- public List<String> getPackageNames() {
- List<String> packageNames = mBundle.getStringArrayList(PACKAGE_NAME_FIELD);
- if (packageNames == null) {
- return Collections.emptyList();
- }
- return Collections.unmodifiableList(packageNames);
- }
-
/** Returns the number of results per page in the result set. */
public int getResultCountPerPage() {
return mBundle.getInt(NUM_PER_PAGE_FIELD, DEFAULT_NUM_PER_PAGE);
@@ -270,11 +251,10 @@
@NonNull
public Map<String, List<String>> getProjections() {
Bundle typePropertyPathsBundle = mBundle.getBundle(PROJECTION_TYPE_PROPERTY_PATHS_FIELD);
- Set<String> schemaTypes = typePropertyPathsBundle.keySet();
- Map<String, List<String>> typePropertyPathsMap = new ArrayMap<>(schemaTypes.size());
- for (String schemaType : schemaTypes) {
- typePropertyPathsMap.put(
- schemaType, typePropertyPathsBundle.getStringArrayList(schemaType));
+ Set<String> schemas = typePropertyPathsBundle.keySet();
+ Map<String, List<String>> typePropertyPathsMap = new ArrayMap<>(schemas.size());
+ for (String schema : schemas) {
+ typePropertyPathsMap.put(schema, typePropertyPathsBundle.getStringArrayList(schema));
}
return typePropertyPathsMap;
}
@@ -283,7 +263,7 @@
public static final class Builder {
private final Bundle mBundle;
- private final ArrayList<String> mSchemaTypes = new ArrayList<>();
+ private final ArrayList<String> mSchemas = new ArrayList<>();
private final ArrayList<String> mNamespaces = new ArrayList<>();
private final ArrayList<String> mPackageNames = new ArrayList<>();
private final Bundle mProjectionTypePropertyMasks = new Bundle();
@@ -312,10 +292,10 @@
* <p>If unset, the query will search over all schema types.
*/
@NonNull
- public Builder addSchemaType(@NonNull String... schemaTypes) {
- Preconditions.checkNotNull(schemaTypes);
+ public Builder addFilterSchemas(@NonNull String... schemas) {
+ Preconditions.checkNotNull(schemas);
Preconditions.checkState(!mBuilt, "Builder has already been used");
- return addSchemaType(Arrays.asList(schemaTypes));
+ return addFilterSchemas(Arrays.asList(schemas));
}
/**
@@ -325,10 +305,10 @@
* <p>If unset, the query will search over all schema types.
*/
@NonNull
- public Builder addSchemaType(@NonNull Collection<String> schemaTypes) {
- Preconditions.checkNotNull(schemaTypes);
+ public Builder addFilterSchemas(@NonNull Collection<String> schemas) {
+ Preconditions.checkNotNull(schemas);
Preconditions.checkState(!mBuilt, "Builder has already been used");
- mSchemaTypes.addAll(schemaTypes);
+ mSchemas.addAll(schemas);
return this;
}
@@ -339,10 +319,10 @@
* <p>If unset, the query will search over all namespaces.
*/
@NonNull
- public Builder addNamespace(@NonNull String... namespaces) {
+ public Builder addFilterNamespaces(@NonNull String... namespaces) {
Preconditions.checkNotNull(namespaces);
Preconditions.checkState(!mBuilt, "Builder has already been used");
- return addNamespace(Arrays.asList(namespaces));
+ return addFilterNamespaces(Arrays.asList(namespaces));
}
/**
@@ -352,7 +332,7 @@
* <p>If unset, the query will search over all namespaces.
*/
@NonNull
- public Builder addNamespace(@NonNull Collection<String> namespaces) {
+ public Builder addFilterNamespaces(@NonNull Collection<String> namespaces) {
Preconditions.checkNotNull(namespaces);
Preconditions.checkState(!mBuilt, "Builder has already been used");
mNamespaces.addAll(namespaces);
@@ -367,9 +347,6 @@
* package names are specified which caller doesn't have access to, then those package names
* will be ignored.
*/
- // Getter is called "getFilterPackageNames" (as opposed to the suggested
- // "getFilterPackageNameses")
- @SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder addFilterPackageNames(@NonNull String... packageNames) {
Preconditions.checkNotNull(packageNames);
@@ -385,9 +362,6 @@
* package names are specified which caller doesn't have access to, then those package names
* will be ignored.
*/
- // Getter is called "getFilterPackageNames" (as opposed to the suggested
- // "getFilterPackageNameses")
- @SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder addFilterPackageNames(@NonNull Collection<String> packageNames) {
Preconditions.checkNotNull(packageNames);
@@ -537,7 +511,7 @@
* type property paths:
*
* <pre>{@code
- * {schemaType: "Email", ["subject", "sender.name", "recipients.name"]}
+ * {schema: "Email", ["subject", "sender.name", "recipients.name"]}
* }</pre>
*
* <p>The above document will be returned as:
@@ -561,9 +535,9 @@
*/
@NonNull
public SearchSpec.Builder addProjection(
- @NonNull String schemaType, @NonNull String... propertyPaths) {
+ @NonNull String schema, @NonNull String... propertyPaths) {
Preconditions.checkNotNull(propertyPaths);
- return addProjection(schemaType, Arrays.asList(propertyPaths));
+ return addProjection(schema, Arrays.asList(propertyPaths));
}
/**
@@ -583,16 +557,16 @@
*/
@NonNull
public SearchSpec.Builder addProjection(
- @NonNull String schemaType, @NonNull Collection<String> propertyPaths) {
+ @NonNull String schema, @NonNull Collection<String> propertyPaths) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(schemaType);
+ Preconditions.checkNotNull(schema);
Preconditions.checkNotNull(propertyPaths);
ArrayList<String> propertyPathsArrayList = new ArrayList<>(propertyPaths.size());
for (String propertyPath : propertyPaths) {
Preconditions.checkNotNull(propertyPath);
propertyPathsArrayList.add(propertyPath);
}
- mProjectionTypePropertyMasks.putStringArrayList(schemaType, propertyPathsArrayList);
+ mProjectionTypePropertyMasks.putStringArrayList(schema, propertyPathsArrayList);
return this;
}
@@ -608,7 +582,7 @@
throw new IllegalSearchSpecException("Missing termMatchType field.");
}
mBundle.putStringArrayList(NAMESPACE_FIELD, mNamespaces);
- mBundle.putStringArrayList(SCHEMA_TYPE_FIELD, mSchemaTypes);
+ mBundle.putStringArrayList(SCHEMA_FIELD, mSchemas);
mBundle.putStringArrayList(PACKAGE_NAME_FIELD, mPackageNames);
mBundle.putBundle(PROJECTION_TYPE_PROPERTY_PATHS_FIELD, mProjectionTypePropertyMasks);
mBuilt = true;
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
index 1486df3..2caa94a5e 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
@@ -126,9 +126,9 @@
* <p>Any documents of these types will be visible on system UI surfaces by default.
*/
@NonNull
- public Builder addSchema(@NonNull AppSearchSchema... schemas) {
+ public Builder addSchemas(@NonNull AppSearchSchema... schemas) {
Preconditions.checkNotNull(schemas);
- return addSchema(Arrays.asList(schemas));
+ return addSchemas(Arrays.asList(schemas));
}
/**
@@ -137,7 +137,7 @@
* <p>Any documents of these types will be visible on system UI surfaces by default.
*/
@NonNull
- public Builder addSchema(@NonNull Collection<AppSearchSchema> schemas) {
+ public Builder addSchemas(@NonNull Collection<AppSearchSchema> schemas) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(schemas);
mSchemas.addAll(schemas);
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 90a6f60..0328d54 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
@@ -179,9 +179,9 @@
/** Adds migratedTypes to the list of migrated schema types. */
@NonNull
- public Builder addMigratedType(@NonNull String migratedType) {
+ public Builder addMigratedType(@NonNull Collection<String> migratedTypes) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- mMigratedTypes.add(Preconditions.checkNotNull(migratedType));
+ mMigratedTypes.addAll(Preconditions.checkNotNull(migratedTypes));
return this;
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
index 592b8b9..8bff720 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
@@ -195,8 +195,7 @@
mIcingSearchEngineLocked = new IcingSearchEngine(options);
mVisibilityStoreLocked =
- new VisibilityStore(
- this, context, userId, globalQuerierPackage);
+ new VisibilityStore(this, context, userId, globalQuerierPackage);
InitializeResultProto initializeResultProto = mIcingSearchEngineLocked.initialize();
SchemaProto schemaProto;
@@ -508,8 +507,8 @@
@NonNull String queryExpression,
@NonNull SearchSpec searchSpec)
throws AppSearchException {
- if (!searchSpec.getPackageNames().isEmpty()
- && !searchSpec.getPackageNames().contains(packageName)) {
+ List<String> filterPackageNames = searchSpec.getFilterPackageNames();
+ if (!filterPackageNames.isEmpty() && !filterPackageNames.contains(packageName)) {
// Client wanted to query over some packages that weren't its own. This isn't
// allowed through local query so we can return early with no results.
return new SearchResultPage(Bundle.EMPTY);
@@ -553,7 +552,7 @@
throws AppSearchException {
mReadWriteLock.readLock().lock();
try {
- Set<String> packageFilters = new ArraySet<>(searchSpec.getPackageNames());
+ Set<String> packageFilters = new ArraySet<>(searchSpec.getFilterPackageNames());
Set<String> prefixFilters = new ArraySet<>();
Set<String> allPrefixes = mNamespaceMapLocked.keySet();
if (packageFilters.isEmpty()) {
@@ -573,7 +572,7 @@
// Find which schemas the client is allowed to query over.
Set<String> allowedPrefixedSchemas = new ArraySet<>();
- List<String> schemaFilters = searchSpec.getSchemaTypes();
+ List<String> schemaFilters = searchSpec.getFilterSchemas();
for (String prefix : prefixFilters) {
String packageName = getPackageName(prefix);
@@ -752,8 +751,8 @@
@NonNull String queryExpression,
@NonNull SearchSpec searchSpec)
throws AppSearchException {
- if (!searchSpec.getPackageNames().isEmpty()
- && !searchSpec.getPackageNames().contains(packageName)) {
+ List<String> filterPackageNames = searchSpec.getFilterPackageNames();
+ if (!filterPackageNames.isEmpty() && !filterPackageNames.contains(packageName)) {
// We're only removing documents within the parameter `packageName`. If we're not
// restricting our remove-query to this package name, then there's nothing for us to
// remove.
@@ -1085,7 +1084,7 @@
Set<String> allowedPrefixedSchemas = new ArraySet<>();
// Add all the schema filters the client specified.
- List<String> schemaFilters = searchSpec.getSchemaTypes();
+ List<String> schemaFilters = searchSpec.getFilterSchemas();
for (int i = 0; i < schemaFilters.size(); i++) {
allowedPrefixedSchemas.add(prefix + schemaFilters.get(i));
}
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
new file mode 100644
index 0000000..b3f8264
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 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.external.localstorage;
+
+import static android.app.appsearch.AppSearchResult.throwableToFailedResult;
+
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchBatchResult;
+import android.app.appsearch.AppSearchMigrationHelper;
+import android.app.appsearch.GenericDocument;
+import android.app.appsearch.SearchResultPage;
+import android.app.appsearch.SearchSpec;
+import android.app.appsearch.SetSchemaResponse;
+import android.app.appsearch.exceptions.AppSearchException;
+import android.os.Bundle;
+import android.os.Parcel;
+
+import com.android.internal.util.Preconditions;
+
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.CodedOutputStream;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+/**
+ * An implementation of {@link AppSearchMigrationHelper} which query document and save post-migrated
+ * documents to locally in the app's storage space.
+ */
+class AppSearchMigrationHelperImpl implements AppSearchMigrationHelper {
+ private final AppSearchImpl mAppSearchImpl;
+ private final String mPackageName;
+ private final String mDatabaseName;
+ private final File mFile;
+ private final Map<String, Integer> mCurrentVersionMap;
+ private final Map<String, Integer> mFinalVersionMap;
+
+ AppSearchMigrationHelperImpl(
+ @NonNull AppSearchImpl appSearchImpl,
+ @NonNull Map<String, Integer> currentVersionMap,
+ @NonNull Map<String, Integer> finalVersionMap,
+ @NonNull String packageName,
+ @NonNull String databaseName)
+ throws IOException {
+ mAppSearchImpl = Preconditions.checkNotNull(appSearchImpl);
+ mCurrentVersionMap = Preconditions.checkNotNull(currentVersionMap);
+ mFinalVersionMap = Preconditions.checkNotNull(finalVersionMap);
+ mPackageName = Preconditions.checkNotNull(packageName);
+ mDatabaseName = Preconditions.checkNotNull(databaseName);
+ mFile = File.createTempFile(/*prefix=*/ "appsearch", /*suffix=*/ null);
+ }
+
+ @Override
+ public void queryAndTransform(
+ @NonNull String schemaType, @NonNull AppSearchMigrationHelper.Transformer migrator)
+ throws Exception {
+ Preconditions.checkState(mFile.exists(), "Internal temp file does not exist.");
+ int currentVersion = mCurrentVersionMap.get(schemaType);
+ int finalVersion = mFinalVersionMap.get(schemaType);
+ try (FileOutputStream outputStream = new FileOutputStream(mFile)) {
+ // TODO(b/151178558) change the output stream so that we can use it in platform
+ CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(outputStream);
+ SearchResultPage searchResultPage =
+ mAppSearchImpl.query(
+ mPackageName,
+ mDatabaseName,
+ /*queryExpression=*/ "",
+ new SearchSpec.Builder()
+ .addFilterSchemas(schemaType)
+ .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+ .build());
+ while (!searchResultPage.getResults().isEmpty()) {
+ for (int i = 0; i < searchResultPage.getResults().size(); i++) {
+ GenericDocument newDocument =
+ migrator.transform(
+ currentVersion,
+ finalVersion,
+ searchResultPage.getResults().get(i).getDocument());
+ Bundle bundle = newDocument.getBundle();
+ Parcel parcel = Parcel.obtain();
+ parcel.writeBundle(bundle);
+ byte[] serializedMessage = parcel.marshall();
+ parcel.recycle();
+ codedOutputStream.writeByteArrayNoTag(serializedMessage);
+ }
+ codedOutputStream.flush();
+ searchResultPage = mAppSearchImpl.getNextPage(searchResultPage.getNextPageToken());
+ outputStream.flush();
+ }
+ }
+ }
+
+ /**
+ * Reads {@link GenericDocument} from the temperate file and saves them to AppSearch.
+ *
+ * <p>This method should be only called once.
+ *
+ * @return the {@link AppSearchBatchResult} for migration documents.
+ */
+ @NonNull
+ public SetSchemaResponse readAndPutDocuments(SetSchemaResponse.Builder responseBuilder)
+ throws IOException, AppSearchException {
+ Preconditions.checkState(mFile.exists(), "Internal temp file does not exist.");
+ try (InputStream inputStream = new FileInputStream(mFile)) {
+ CodedInputStream codedInputStream = CodedInputStream.newInstance(inputStream);
+ while (!codedInputStream.isAtEnd()) {
+ GenericDocument document = readDocumentFromInputStream(codedInputStream);
+ try {
+ mAppSearchImpl.putDocument(mPackageName, mDatabaseName, document);
+ } catch (Throwable t) {
+ responseBuilder.setFailure(
+ document.getSchemaType(),
+ document.getNamespace(),
+ document.getUri(),
+ throwableToFailedResult(t));
+ }
+ }
+ mAppSearchImpl.persistToDisk();
+ return responseBuilder.build();
+ } finally {
+ mFile.delete();
+ }
+ }
+
+ void deleteTempFile() {
+ mFile.delete();
+ }
+
+ /**
+ * Reads {@link GenericDocument} from given {@link CodedInputStream}.
+ *
+ * @param codedInputStream The codedInputStream to read from
+ * @throws IOException on File operation error.
+ */
+ @NonNull
+ private static GenericDocument readDocumentFromInputStream(
+ @NonNull CodedInputStream codedInputStream) throws IOException {
+ byte[] serializedMessage = codedInputStream.readByteArray();
+
+ Parcel parcel = Parcel.obtain();
+ parcel.unmarshall(serializedMessage, 0, serializedMessage.length);
+ parcel.setDataPosition(0);
+ Bundle bundle = parcel.readBundle();
+ parcel.recycle();
+
+ return new GenericDocument(bundle);
+ }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java
index 07d50ae..3b5e275 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java
@@ -40,8 +40,8 @@
Preconditions.checkNotNull(spec);
SearchSpecProto.Builder protoBuilder =
SearchSpecProto.newBuilder()
- .addAllSchemaTypeFilters(spec.getSchemaTypes())
- .addAllNamespaceFilters(spec.getNamespaces());
+ .addAllSchemaTypeFilters(spec.getFilterSchemas())
+ .addAllNamespaceFilters(spec.getFilterNamespaces());
@SearchSpec.TermMatch int termMatchCode = spec.getTermMatch();
TermMatchType.Code termMatchCodeProto = TermMatchType.Code.forNumber(termMatchCode);
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index 2774181..12699b7 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-I3fd4c96bf775c2539d744c416cdbf1d3c9544f03
+Ibe06fb9c574c8718191f833bb042fa10c300e4e2
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
index f8d0d80..afa633a 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
@@ -102,10 +102,10 @@
@Override
@NonNull
- public ListenableFuture<AppSearchBatchResult<String, Void>> putDocuments(
+ public ListenableFuture<AppSearchBatchResult<String, Void>> put(
@NonNull PutDocumentsRequest request) {
SettableFuture<AppSearchBatchResult<String, Void>> future = SettableFuture.create();
- mAppSearchSession.putDocuments(
+ mAppSearchSession.put(
request, mExecutor, new BatchResultCallbackAdapter<>(future));
return future;
}
@@ -122,10 +122,10 @@
@Override
@NonNull
- public SearchResultsShim query(
+ public SearchResultsShim search(
@NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
SearchResults searchResults =
- mAppSearchSession.query(queryExpression, searchSpec, mExecutor);
+ mAppSearchSession.search(queryExpression, searchSpec, mExecutor);
return new SearchResultsShimImpl(searchResults, mExecutor);
}
@@ -139,19 +139,19 @@
@Override
@NonNull
- public ListenableFuture<AppSearchBatchResult<String, Void>> removeByUri(
+ public ListenableFuture<AppSearchBatchResult<String, Void>> remove(
@NonNull RemoveByUriRequest request) {
SettableFuture<AppSearchBatchResult<String, Void>> future = SettableFuture.create();
- mAppSearchSession.removeByUri(request, mExecutor, new BatchResultCallbackAdapter<>(future));
+ mAppSearchSession.remove(request, mExecutor, new BatchResultCallbackAdapter<>(future));
return future;
}
@Override
@NonNull
- public ListenableFuture<Void> removeByQuery(
+ public ListenableFuture<Void> remove(
@NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
SettableFuture<AppSearchResult<Void>> future = SettableFuture.create();
- mAppSearchSession.removeByQuery(queryExpression, searchSpec, mExecutor, future::set);
+ mAppSearchSession.remove(queryExpression, searchSpec, mExecutor, future::set);
return Futures.transformAsync(future, this::transformResult, mExecutor);
}
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java
index 39ca687..eb1623e 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java
@@ -67,10 +67,10 @@
@NonNull
@Override
- public SearchResultsShim query(
+ public SearchResultsShim search(
@NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
SearchResults searchResults =
- mGlobalSearchSession.query(queryExpression, searchSpec, mExecutor);
+ mGlobalSearchSession.search(queryExpression, searchSpec, mExecutor);
return new SearchResultsShimImpl(searchResults, mExecutor);
}
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
index e8ea6ef..8e62c0e 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
@@ -33,7 +33,7 @@
public interface AppSearchSessionShim extends Closeable {
/**
- * Sets the schema that will be used by documents provided to the {@link #putDocuments} method.
+ * Sets the schema that will be used by documents provided to the {@link #put} method.
*
* <p>The schema provided here is compared to the stored copy of the schema previously supplied
* to {@link #setSchema}, if any, to determine how to treat existing documents. The following
@@ -143,8 +143,7 @@
* they were successfully indexed, or a failed {@link AppSearchResult} otherwise.
*/
@NonNull
- ListenableFuture<AppSearchBatchResult<String, Void>> putDocuments(
- @NonNull PutDocumentsRequest request);
+ ListenableFuture<AppSearchBatchResult<String, Void>> put(@NonNull PutDocumentsRequest request);
/**
* Retrieves {@link GenericDocument}s by URI.
@@ -161,7 +160,7 @@
@NonNull GetByUriRequest request);
/**
- * Searches a document based on a given query string.
+ * Searches for documents based on a given query string.
*
* <p>Currently we support following features in the raw query format:
*
@@ -200,7 +199,7 @@
* @return The search result of performing this operation.
*/
@NonNull
- SearchResultsShim query(@NonNull String queryExpression, @NonNull SearchSpec searchSpec);
+ SearchResultsShim search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec);
/**
* Reports usage of a particular document by URI and namespace.
@@ -208,7 +207,7 @@
* <p>A usage report represents an event in which a user interacted with or viewed a document.
*
* <p>For each call to {@link #reportUsage}, AppSearch updates usage count and usage recency
- * metrics for that particular document. These metrics are used for ordering {@link #query}
+ * metrics for that particular document. These metrics are used for ordering {@link #search}
* results by the {@link SearchSpec#RANKING_STRATEGY_USAGE_COUNT} and {@link
* SearchSpec#RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP} ranking strategies.
*
@@ -231,13 +230,13 @@
* {@link AppSearchResult} with a result code of {@link AppSearchResult#RESULT_NOT_FOUND}.
*/
@NonNull
- ListenableFuture<AppSearchBatchResult<String, Void>> removeByUri(
+ ListenableFuture<AppSearchBatchResult<String, Void>> remove(
@NonNull RemoveByUriRequest request);
/**
* Removes {@link GenericDocument}s from the index by Query. Documents will be removed if they
* match the {@code queryExpression} in given namespaces and schemaTypes which is set via {@link
- * SearchSpec.Builder#addNamespace} and {@link SearchSpec.Builder#addSchemaType}.
+ * SearchSpec.Builder#addFilterNamespaces} and {@link SearchSpec.Builder#addFilterSchemas}.
*
* <p>An empty {@code queryExpression} matches all documents.
*
@@ -251,8 +250,7 @@
* @return The pending result of performing this operation.
*/
@NonNull
- ListenableFuture<Void> removeByQuery(
- @NonNull String queryExpression, @NonNull SearchSpec searchSpec);
+ ListenableFuture<Void> remove(@NonNull String queryExpression, @NonNull SearchSpec searchSpec);
/**
* Flush all schema and document updates, additions, and deletes to disk if possible.
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java
index cd867a4..b96f99e 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java
@@ -23,7 +23,7 @@
/**
* This class provides global access to the centralized AppSearch index maintained by the system.
*
- * <p>Apps can retrieve indexed documents through the query API.
+ * <p>Apps can retrieve indexed documents through the {@link #search} API.
*/
public interface GlobalSearchSessionShim extends Closeable {
/**
@@ -66,7 +66,7 @@
* @return The search result of performing this operation.
*/
@NonNull
- SearchResultsShim query(@NonNull String queryExpression, @NonNull SearchSpec searchSpec);
+ SearchResultsShim search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec);
/** Closes the {@link GlobalSearchSessionShim}. */
@Override
diff --git a/core/api/current.txt b/core/api/current.txt
index 8dae210..d60bf2c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -657,7 +657,7 @@
field @Deprecated public static final int fontProviderCerts = 16844125; // 0x101055d
field @Deprecated public static final int fontProviderPackage = 16844119; // 0x1010557
field @Deprecated public static final int fontProviderQuery = 16844113; // 0x1010551
- field public static final int fontProviderSystemFontFamily = 16844322; // 0x1010622
+ field public static final int fontProviderSystemFontFamily = 16844321; // 0x1010621
field public static final int fontStyle = 16844095; // 0x101053f
field public static final int fontVariationSettings = 16844144; // 0x1010570
field public static final int fontWeight = 16844083; // 0x1010533
@@ -722,7 +722,7 @@
field public static final int gwpAsanMode = 16844310; // 0x1010616
field public static final int hand_hour = 16843011; // 0x1010103
field public static final int hand_minute = 16843012; // 0x1010104
- field public static final int hand_second = 16844323; // 0x1010623
+ field public static final int hand_second = 16844322; // 0x1010622
field public static final int handle = 16843354; // 0x101025a
field public static final int handleProfiling = 16842786; // 0x1010022
field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e
@@ -1057,11 +1057,11 @@
field public static final int parentActivityName = 16843687; // 0x10103a7
field @Deprecated public static final int password = 16843100; // 0x101015c
field public static final int path = 16842794; // 0x101002a
- field public static final int pathAdvancedPattern = 16844320; // 0x1010620
+ field public static final int pathAdvancedPattern = 16844319; // 0x101061f
field public static final int pathData = 16843781; // 0x1010405
field public static final int pathPattern = 16842796; // 0x101002c
field public static final int pathPrefix = 16842795; // 0x101002b
- field public static final int pathSuffix = 16844318; // 0x101061e
+ field public static final int pathSuffix = 16844317; // 0x101061d
field public static final int patternPathData = 16843978; // 0x10104ca
field public static final int permission = 16842758; // 0x1010006
field public static final int permissionFlags = 16843719; // 0x10103c7
@@ -1154,7 +1154,7 @@
field public static final int reqNavigation = 16843306; // 0x101022a
field public static final int reqTouchScreen = 16843303; // 0x1010227
field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603
- field public static final int requireDeviceScreenOn = 16844317; // 0x101061d
+ field public static final int requireDeviceScreenOn = 16844316; // 0x101061c
field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
field public static final int required = 16843406; // 0x101028e
field public static final int requiredAccountType = 16843734; // 0x10103d6
@@ -1295,10 +1295,10 @@
field public static final int spotShadowAlpha = 16843967; // 0x10104bf
field public static final int src = 16843033; // 0x1010119
field public static final int ssp = 16843747; // 0x10103e3
- field public static final int sspAdvancedPattern = 16844321; // 0x1010621
+ field public static final int sspAdvancedPattern = 16844320; // 0x1010620
field public static final int sspPattern = 16843749; // 0x10103e5
field public static final int sspPrefix = 16843748; // 0x10103e4
- field public static final int sspSuffix = 16844319; // 0x101061f
+ field public static final int sspSuffix = 16844318; // 0x101061e
field public static final int stackFromBottom = 16843005; // 0x10100fd
field public static final int stackViewStyle = 16843838; // 0x101043e
field public static final int starStyle = 16842882; // 0x1010082
@@ -21108,7 +21108,9 @@
public static final class MediaCodecInfo.AudioCapabilities {
method public android.util.Range<java.lang.Integer> getBitrateRange();
+ method @NonNull public android.util.Range<java.lang.Integer>[] getInputChannelCountRanges();
method public int getMaxInputChannelCount();
+ method public int getMinInputChannelCount();
method public android.util.Range<java.lang.Integer>[] getSupportedSampleRateRanges();
method public int[] getSupportedSampleRates();
method public boolean isSampleRateSupported(int);
@@ -49586,6 +49588,7 @@
field public static final int FLAGS_CHANGED = 4; // 0x4
field public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 1; // 0x1
field public static final int FLAG_ALT_FOCUSABLE_IM = 131072; // 0x20000
+ field @Deprecated public static final int FLAG_BLUR_BEHIND = 4; // 0x4
field public static final int FLAG_DIM_BEHIND = 2; // 0x2
field @Deprecated public static final int FLAG_DISMISS_KEYGUARD = 4194304; // 0x400000
field @Deprecated public static final int FLAG_DITHER = 4096; // 0x1000
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index e8650fa..be3c246 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -208,6 +208,16 @@
method public void teardownTestNetwork(@NonNull android.net.Network);
}
+ public final class UnderlyingNetworkInfo implements android.os.Parcelable {
+ ctor public UnderlyingNetworkInfo(int, @NonNull String, @NonNull java.util.List<java.lang.String>);
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.UnderlyingNetworkInfo> CREATOR;
+ field @NonNull public final String iface;
+ field public final int ownerUid;
+ field @NonNull public final java.util.List<java.lang.String> underlyingIfaces;
+ }
+
}
package android.os {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 683bc84..4359495 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -263,7 +263,6 @@
field public static final String UPDATE_TIME_ZONE_RULES = "android.permission.UPDATE_TIME_ZONE_RULES";
field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
field public static final String USER_ACTIVITY = "android.permission.USER_ACTIVITY";
- field public static final String USE_BACKGROUND_BLUR = "android.permission.USE_BACKGROUND_BLUR";
field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
field public static final String WHITELIST_AUTO_REVOKE_PERMISSIONS = "android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS";
field public static final String WHITELIST_RESTRICTED_PERMISSIONS = "android.permission.WHITELIST_RESTRICTED_PERMISSIONS";
@@ -295,8 +294,6 @@
field public static final int sdkVersion = 16844304; // 0x1010610
field public static final int supportsAmbientMode = 16844173; // 0x101058d
field public static final int userRestriction = 16844164; // 0x1010584
- field public static final int windowBackgroundBlurEnabled = 16844316; // 0x101061c
- field public static final int windowBackgroundBlurRadius = 16844315; // 0x101061b
}
public static final class R.bool {
@@ -2759,18 +2756,6 @@
}
-package android.graphics.drawable {
-
- public final class BackgroundBlurDrawable extends android.graphics.drawable.Drawable {
- ctor @RequiresPermission(android.Manifest.permission.USE_BACKGROUND_BLUR) public BackgroundBlurDrawable();
- method public void setBlurRadius(int);
- method public void setColor(@ColorInt int);
- method public void setCornerRadius(float);
- method public void setCornerRadius(float, float, float, float);
- }
-
-}
-
package android.graphics.fonts {
public class FontManager {
@@ -14102,10 +14087,8 @@
method public boolean isSystemApplicationOverlay();
method @RequiresPermission(android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY) public void setSystemApplicationOverlay(boolean);
method public final void setUserActivityTimeout(long);
- field @RequiresPermission(android.Manifest.permission.USE_BACKGROUND_BLUR) public static final int FLAG_BLUR_BEHIND = 4; // 0x4
field @RequiresPermission(android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS) public static final int SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 524288; // 0x80000
field @RequiresPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW) public static final int SYSTEM_FLAG_SHOW_FOR_ALL_USERS = 16; // 0x10
- field @RequiresPermission(android.Manifest.permission.USE_BACKGROUND_BLUR) public int backgroundBlurRadius;
}
@IntDef(flag=true, prefix={"SYSTEM_FLAG_"}, value={android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowManager.LayoutParams.SystemFlags {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 7e7f887..b51d4ac 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -59,6 +59,7 @@
import android.content.pm.PackageInstaller;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageUserState;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
@@ -70,6 +71,12 @@
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VersionedPackage;
import android.content.pm.dex.ArtManager;
+import android.content.pm.parsing.PackageInfoWithoutStateUtils;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
@@ -116,6 +123,7 @@
import libcore.util.EmptyArray;
+import java.io.File;
import java.lang.ref.WeakReference;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
@@ -2055,6 +2063,31 @@
return info.loadLabel(this);
}
+ @Nullable
+ public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath,
+ @PackageInfoFlags int flags) {
+ if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE)) == 0) {
+ // Caller expressed no opinion about what encryption
+ // aware/unaware components they want to see, so match both
+ flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+ }
+
+ boolean collectCertificates = (flags & PackageManager.GET_SIGNATURES) != 0
+ || (flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0;
+
+ ParseInput input = ParseTypeImpl.forParsingWithoutPlatformCompat().reset();
+ ParseResult<ParsingPackage> result = ParsingPackageUtils.parseDefault(input,
+ new File(archiveFilePath), 0, getPermissionManager().getSplitPermissions(),
+ collectCertificates);
+ if (result.isError()) {
+ return null;
+ }
+ return PackageInfoWithoutStateUtils.generate(result.getResult(), null, flags, 0, 0, null,
+ new PackageUserState(), UserHandle.getCallingUserId());
+ }
+
@Override
public int installExistingPackage(String packageName) throws NameNotFoundException {
return installExistingPackage(packageName, INSTALL_REASON_UNKNOWN);
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index c2c62c1..9d3286f 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -96,7 +96,14 @@
oneway void setInheritShowWhenLocked(in IBinder token, boolean setInheritShownWhenLocked);
oneway void setTurnScreenOn(in IBinder token, boolean turnScreenOn);
oneway void reportActivityFullyDrawn(in IBinder token, boolean restoredFromBundle);
- oneway void overridePendingTransition(in IBinder token, in String packageName,
+ /**
+ * Overrides the animation of activity pending transition. This call is not one-way because
+ * the method is usually used after startActivity or Activity#finish. If this is non-blocking,
+ * the calling activity may proceed to complete pause and become stopping state, which will
+ * cause the request to be ignored. Besides, startActivity and Activity#finish are blocking
+ * calls, so this method should be the same as them to keep the invocation order.
+ */
+ void overridePendingTransition(in IBinder token, in String packageName,
int enterAnim, int exitAnim);
int setVrMode(in IBinder token, boolean enabled, in ComponentName packageName);
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index e31e024..390d921 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -337,6 +337,23 @@
}
/**
+ * @return {@code true} if parameters that are important for size compat have changed.
+ * @hide
+ */
+ public boolean equalsForSizeCompat(@Nullable TaskInfo that) {
+ if (that == null) {
+ return false;
+ }
+ return displayId == that.displayId
+ && taskId == that.taskId
+ && topActivityInSizeCompat == that.topActivityInSizeCompat
+ // TopActivityToken and bounds are important if top activity is in size compat
+ && (!topActivityInSizeCompat || topActivityToken.equals(that.topActivityToken))
+ && (!topActivityInSizeCompat || configuration.windowConfiguration.getBounds()
+ .equals(that.configuration.windowConfiguration.getBounds()));
+ }
+
+ /**
* Reads the TaskInfo from a parcel.
*/
void readFromParcel(Parcel source) {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 72fb1ca..e01e530 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -48,12 +48,6 @@
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.dex.ArtManager;
-import android.content.pm.parsing.PackageInfoWithoutStateUtils;
-import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
@@ -83,7 +77,6 @@
import dalvik.system.VMRuntime;
-import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.security.cert.Certificate;
@@ -6791,25 +6784,8 @@
@Nullable
public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath,
@PackageInfoFlags int flags) {
- if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_DIRECT_BOOT_AWARE)) == 0) {
- // Caller expressed no opinion about what encryption
- // aware/unaware components they want to see, so match both
- flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
- }
-
- boolean collectCertificates = (flags & PackageManager.GET_SIGNATURES) != 0
- || (flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0;
-
- ParseInput input = ParseTypeImpl.forParsingWithoutPlatformCompat().reset();
- ParseResult<ParsingPackage> result = ParsingPackageUtils.parseDefault(input,
- new File(archiveFilePath), 0, collectCertificates);
- if (result.isError()) {
- return null;
- }
- return PackageInfoWithoutStateUtils.generate(result.getResult(), null, flags, 0, 0, null,
- new PackageUserState(), UserHandle.getCallingUserId());
+ throw new UnsupportedOperationException(
+ "getPackageArchiveInfo() not implemented in subclass");
}
/**
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index b054304..8fbf287 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -34,7 +34,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StyleableRes;
-import android.app.ActivityThread;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
@@ -222,13 +221,15 @@
private static final int MAX_FILE_NAME_SIZE = 223;
/**
- * @see #parseDefault(ParseInput, File, int, boolean)
+ * @see #parseDefault(ParseInput, File, int, List, boolean)
*/
@NonNull
public static ParseResult<ParsingPackage> parseDefaultOneTime(File file,
- @ParseFlags int parseFlags, boolean collectCertificates) {
+ @ParseFlags int parseFlags,
+ @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions,
+ boolean collectCertificates) {
ParseInput input = ParseTypeImpl.forDefaultParsing().reset();
- return parseDefault(input, file, parseFlags, collectCertificates);
+ return parseDefault(input, file, parseFlags, splitPermissions, collectCertificates);
}
/**
@@ -238,28 +239,32 @@
*/
@NonNull
public static ParseResult<ParsingPackage> parseDefault(ParseInput input, File file,
- @ParseFlags int parseFlags, boolean collectCertificates) {
+ @ParseFlags int parseFlags,
+ @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions,
+ boolean collectCertificates) {
ParseResult<ParsingPackage> result;
- ParsingPackageUtils parser = new ParsingPackageUtils(false, null, null, new Callback() {
- @Override
- public boolean hasFeature(String feature) {
- // Assume the device doesn't support anything. This will affect permission parsing
- // and will force <uses-permission/> declarations to include all requiredNotFeature
- // permissions and exclude all requiredFeature permissions. This mirrors the old
- // behavior.
- return false;
- }
+ ParsingPackageUtils parser = new ParsingPackageUtils(false, null, null, splitPermissions,
+ new Callback() {
+ @Override
+ public boolean hasFeature(String feature) {
+ // Assume the device doesn't support anything. This will affect permission
+ // parsing and will force <uses-permission/> declarations to include all
+ // requiredNotFeature permissions and exclude all requiredFeature
+ // permissions. This mirrors the old behavior.
+ return false;
+ }
- @Override
- public ParsingPackage startParsingPackage(
- @NonNull String packageName,
- @NonNull String baseApkPath,
- @NonNull String path,
- @NonNull TypedArray manifestArray, boolean isCoreApp) {
- return new ParsingPackageImpl(packageName, baseApkPath, path, manifestArray);
- }
- });
+ @Override
+ public ParsingPackage startParsingPackage(
+ @NonNull String packageName,
+ @NonNull String baseApkPath,
+ @NonNull String path,
+ @NonNull TypedArray manifestArray, boolean isCoreApp) {
+ return new ParsingPackageImpl(packageName, baseApkPath, path,
+ manifestArray);
+ }
+ });
try {
result = parser.parsePackage(input, file, parseFlags);
if (result.isError()) {
@@ -290,13 +295,18 @@
private boolean mOnlyCoreApps;
private String[] mSeparateProcesses;
private DisplayMetrics mDisplayMetrics;
+ @NonNull
+ private List<PermissionManager.SplitPermissionInfo> mSplitPermissionInfos;
private Callback mCallback;
public ParsingPackageUtils(boolean onlyCoreApps, String[] separateProcesses,
- DisplayMetrics displayMetrics, @NonNull Callback callback) {
+ DisplayMetrics displayMetrics,
+ @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions,
+ @NonNull Callback callback) {
mOnlyCoreApps = onlyCoreApps;
mSeparateProcesses = separateProcesses;
mDisplayMetrics = displayMetrics;
+ mSplitPermissionInfos = splitPermissions;
mCallback = callback;
}
@@ -2743,14 +2753,10 @@
}
}
- private static void convertSplitPermissions(ParsingPackage pkg) {
- final List<PermissionManager.SplitPermissionInfo> splitPermissions =
- ActivityThread.currentApplication().getSystemService(PermissionManager.class)
- .getSplitPermissions();
-
- final int listSize = splitPermissions.size();
+ private void convertSplitPermissions(ParsingPackage pkg) {
+ final int listSize = mSplitPermissionInfos.size();
for (int is = 0; is < listSize; is++) {
- final PermissionManager.SplitPermissionInfo spi = splitPermissions.get(is);
+ final PermissionManager.SplitPermissionInfo spi = mSplitPermissionInfos.get(is);
List<String> requestedPermissions = pkg.getRequestedPermissions();
if (pkg.getTargetSdkVersion() >= spi.getTargetSdk()
|| !requestedPermissions.contains(spi.getSplitPermission())) {
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 95f1d12..48b05b7 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -47,20 +47,22 @@
* begins adjusting the power state to match what was requested.
* </p>
*
+ * @param groupId The identifier for the display group being requested to change power state
* @param request The requested power state.
- * @param waitForNegativeProximity If true, issues a request to wait for
+ * @param waitForNegativeProximity If {@code true}, issues a request to wait for
* negative proximity before turning the screen back on, assuming the screen
* was turned off by the proximity sensor.
- * @return True if display is ready, false if there are important changes that must
- * be made asynchronously (such as turning the screen on), in which case the caller
- * should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged()}
- * then try the request again later until the state converges.
+ * @return {@code true} if display group is ready, {@code false} if there are important
+ * changes that must be made asynchronously (such as turning the screen on), in which case
+ * the caller should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged}
+ * then try the request again later until the state converges. If the provided {@code groupId}
+ * cannot be found then {@code true} will be returned.
*/
- public abstract boolean requestPowerState(DisplayPowerRequest request,
+ public abstract boolean requestPowerState(int groupId, DisplayPowerRequest request,
boolean waitForNegativeProximity);
/**
- * Returns true if the proximity sensor screen-off function is available.
+ * Returns {@code true} if the proximity sensor screen-off function is available.
*/
public abstract boolean isProximitySensorAvailable();
@@ -71,6 +73,22 @@
public abstract int getDisplayGroupId(int displayId);
/**
+ * Registers a display group listener which will be informed of the addition, removal, or change
+ * of display groups.
+ *
+ * @param listener The listener to register.
+ */
+ public abstract void registerDisplayGroupListener(DisplayGroupListener listener);
+
+ /**
+ * Unregisters a display group listener which will be informed of the addition, removal, or
+ * change of display groups.
+ *
+ * @param listener The listener to unregister.
+ */
+ public abstract void unregisterDisplayGroupListener(DisplayGroupListener listener);
+
+ /**
* Screenshot for internal system-only use such as rotation, etc. This method includes
* secure layers and the result should never be exposed to non-system applications.
* This method does not apply any rotation and provides the output in natural orientation.
diff --git a/core/java/android/net/UnderlyingNetworkInfo.java b/core/java/android/net/UnderlyingNetworkInfo.java
index 8fb4832..7bf9231 100644
--- a/core/java/android/net/UnderlyingNetworkInfo.java
+++ b/core/java/android/net/UnderlyingNetworkInfo.java
@@ -16,11 +16,15 @@
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -30,6 +34,7 @@
*
* @hide
*/
+@SystemApi(client = MODULE_LIBRARIES)
public final class UnderlyingNetworkInfo implements Parcelable {
/** The owner of this network. */
public final int ownerUid;
@@ -46,7 +51,7 @@
Objects.requireNonNull(underlyingIfaces);
this.ownerUid = ownerUid;
this.iface = iface;
- this.underlyingIfaces = underlyingIfaces;
+ this.underlyingIfaces = Collections.unmodifiableList(new ArrayList<>(underlyingIfaces));
}
private UnderlyingNetworkInfo(@NonNull Parcel in) {
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index b229212..790773f 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -65,7 +65,7 @@
DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "true");
DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
- DEFAULT_FLAGS.put("settings_silky_home", "false");
+ DEFAULT_FLAGS.put("settings_silky_home", "true");
DEFAULT_FLAGS.put("settings_contextual_home", "false");
DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "false");
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9a412fc..749c0df 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9806,20 +9806,6 @@
}
}
- private void notifyAttachForDrawables() {
- if (mBackground != null) mBackground.onAttached(this);
- if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) {
- mForegroundInfo.mDrawable.onAttached(this);
- }
- }
-
- private void notifyDetachForDrawables() {
- if (mBackground != null) mBackground.onDetached(this);
- if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) {
- mForegroundInfo.mDrawable.onDetached(this);
- }
- }
-
private void setNotifiedContentCaptureAppeared() {
mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
@@ -10367,6 +10353,7 @@
for (int i = 0; i < childDrawIndex; i++) {
drawingOrderInParent += numViewsForAccessibility(preorderedList.get(i));
}
+ preorderedList.clear();
} else {
final int childIndex = parentGroup.indexOfChild(viewAtDrawingLevel);
final boolean customOrder = parentGroup.isChildrenDrawingOrderEnabled();
@@ -20671,7 +20658,6 @@
notifyEnterOrExitForAutoFillIfNeeded(true);
notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
- notifyAttachForDrawables();
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@@ -20721,7 +20707,6 @@
notifyEnterOrExitForAutoFillIfNeeded(false);
notifyAppearedOrDisappearedForContentCaptureIfNeeded(false);
- notifyDetachForDrawables();
}
/**
@@ -23859,7 +23844,6 @@
if (mBackground != null) {
if (isAttachedToWindow()) {
mBackground.setVisible(false, false);
- mBackground.onDetached(this);
}
mBackground.setCallback(null);
unscheduleDrawable(mBackground);
@@ -23909,7 +23893,6 @@
background.setState(getDrawableState());
}
if (isAttachedToWindow()) {
- background.onAttached(this);
background.setVisible(getWindowVisibility() == VISIBLE && isShown(), false);
}
@@ -24142,7 +24125,6 @@
if (mForegroundInfo.mDrawable != null) {
if (isAttachedToWindow()) {
mForegroundInfo.mDrawable.setVisible(false, false);
- mForegroundInfo.mDrawable.onDetached(this);
}
mForegroundInfo.mDrawable.setCallback(null);
unscheduleDrawable(mForegroundInfo.mDrawable);
@@ -24160,7 +24142,6 @@
}
applyForegroundTint();
if (isAttachedToWindow()) {
- foreground.onAttached(this);
foreground.setVisible(getWindowVisibility() == VISIBLE && isShown(), false);
}
// Set callback last, since the view may still be initializing.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 844fc26..5e3599d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -115,7 +115,6 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.RenderNode;
-import android.graphics.drawable.BackgroundBlurDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.hardware.display.DisplayManager;
@@ -187,6 +186,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.SomeArgs;
@@ -672,14 +672,6 @@
new BackgroundBlurDrawable.Aggregator(this);
/**
- * @return {@link BackgroundBlurDrawable.Aggregator} for this instance.
- */
- @NonNull
- public BackgroundBlurDrawable.Aggregator getBlurRegionAggregator() {
- return mBlurRegionAggregator;
- }
-
- /**
* @return {@link ImeFocusController} for this instance.
*/
@NonNull
@@ -10084,6 +10076,13 @@
}
}
+ /**
+ * Creates a background blur drawable for the backing {@link Surface}.
+ */
+ public BackgroundBlurDrawable createBackgroundBlurDrawable() {
+ return mBlurRegionAggregator.createBackgroundBlurDrawable(mContext);
+ }
+
@Override
public void onDescendantUnbufferedRequested() {
mUnbufferedInputSource = mView.mUnbufferedInputSource;
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 8319b74..63f1eed 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1511,13 +1511,9 @@
* Use {@link #dimAmount} to control the amount of dim. */
public static final int FLAG_DIM_BEHIND = 0x00000002;
- /** Window flag: enable blurring behind this window.
- * To set the amount of blur, use {@link #backgroundBlurRadius}
- *
- * @hide
- */
- @RequiresPermission(permission.USE_BACKGROUND_BLUR)
- @SystemApi
+ /** Window flag: blur everything behind this window.
+ * @deprecated Blurring is no longer supported. */
+ @Deprecated
public static final int FLAG_BLUR_BEHIND = 0x00000004;
/** Window flag: this window won't ever get key input focus, so the
@@ -3237,14 +3233,10 @@
public boolean preferMinimalPostProcessing = false;
/**
- * When {@link FLAG_BLUR_BEHIND} is set, this is the amount of blur in pixels that this
- * window will use to blur behind itself.
- * The range is from 0, which means no blur, to 150.
+ * Indicates that this window wants to have blurred content behind it.
*
* @hide
*/
- @SystemApi
- @RequiresPermission(permission.USE_BACKGROUND_BLUR)
public int backgroundBlurRadius = 0;
/**
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 023d9ff2..20230e7 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -98,9 +98,17 @@
public abstract boolean acceptThirdPartyCookies(WebView webview);
/**
- * Sets a cookie for the given URL. Any existing cookie with the same host,
- * path and name will be replaced with the new cookie. The cookie being set
- * will be ignored if it is expired.
+ * Sets a single cookie (key-value pair) for the given URL. Any existing cookie with the same
+ * host, path and name will be replaced with the new cookie. The cookie being set
+ * will be ignored if it is expired. To set multiple cookies, your application should invoke
+ * this method multiple times.
+ *
+ * <p>The {@code value} parameter must follow the format of the {@code Set-Cookie} HTTP
+ * response header defined by
+ * <a href="https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03">RFC6265bis</a>.
+ * This is a key-value pair of the form {@code "key=value"}, optionally followed by a list of
+ * cookie attributes delimited with semicolons (ex. {@code "key=value; Max-Age=123"}). Please
+ * consult the RFC specification for a list of valid attributes.
*
* <p class="note"><b>Note:</b> if specifying a {@code value} containing the {@code "Secure"}
* attribute, {@code url} must use the {@code "https://"} scheme.
@@ -112,13 +120,20 @@
public abstract void setCookie(String url, String value);
/**
- * Sets a cookie for the given URL. Any existing cookie with the same host,
- * path and name will be replaced with the new cookie. The cookie being set
- * will be ignored if it is expired.
- * <p>
- * This method is asynchronous.
- * If a {@link ValueCallback} is provided,
- * {@link ValueCallback#onReceiveValue(T) onReceiveValue()} will be called on the current
+ * Sets a single cookie (key-value pair) for the given URL. Any existing cookie with the same
+ * host, path and name will be replaced with the new cookie. The cookie being set
+ * will be ignored if it is expired. To set multiple cookies, your application should invoke
+ * this method multiple times.
+ *
+ * <p>The {@code value} parameter must follow the format of the {@code Set-Cookie} HTTP
+ * response header defined by
+ * <a href="https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03">RFC6265bis</a>.
+ * This is a key-value pair of the form {@code "key=value"}, optionally followed by a list of
+ * cookie attributes delimited with semicolons (ex. {@code "key=value; Max-Age=123"}). Please
+ * consult the RFC specification for a list of valid attributes.
+ *
+ * <p>This method is asynchronous. If a {@link ValueCallback} is provided,
+ * {@link ValueCallback#onReceiveValue} will be called on the current
* thread's {@link android.os.Looper} once the operation is complete.
* The value provided to the callback indicates whether the cookie was set successfully.
* You can pass {@code null} as the callback if you don't need to know when the operation
@@ -137,7 +152,10 @@
callback);
/**
- * Gets the cookies for the given URL.
+ * Gets all the cookies for the given URL. This may return multiple key-value pairs if multiple
+ * cookies are associated with this URL, in which case each cookie will be delimited by {@code
+ * "; "} characters (semicolon followed by a space). Each key-value pair will be of the form
+ * {@code "key=value"}.
*
* @param url the URL for which the cookies are requested
* @return value the cookies as a string, using the format of the 'Cookie'
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index af666d8..9a44c05 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -16,6 +16,7 @@
package com.android.internal.content;
+import android.annotation.NonNull;
import android.app.Activity;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -234,7 +235,7 @@
/**
* Called when an existing package is updated or its disabled state changes.
*/
- public void onPackageModified(String packageName) {
+ public void onPackageModified(@NonNull String packageName) {
}
public boolean didSomePackagesChange() {
diff --git a/graphics/java/android/graphics/drawable/BackgroundBlurDrawable.java b/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java
similarity index 78%
rename from graphics/java/android/graphics/drawable/BackgroundBlurDrawable.java
rename to core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java
index 7e75c5b..96dac56 100644
--- a/graphics/java/android/graphics/drawable/BackgroundBlurDrawable.java
+++ b/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java
@@ -14,13 +14,12 @@
* limitations under the License.
*/
-package android.graphics.drawable;
+package com.android.internal.graphics.drawable;
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
+import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
@@ -31,71 +30,59 @@
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RenderNode;
+import android.graphics.drawable.Drawable;
import android.util.ArrayMap;
import android.util.Log;
import android.view.SurfaceControl;
-import android.view.View;
import android.view.ViewRootImpl;
+import com.android.internal.R;
+
/**
* A drawable that keeps track of a blur region, pokes a hole under it, and propagates its state
* to SurfaceFlinger.
- *
- * @hide
*/
-@SystemApi
public final class BackgroundBlurDrawable extends Drawable {
+
private static final String TAG = BackgroundBlurDrawable.class.getSimpleName();
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private final Aggregator mAggregator;
private final RenderNode mRenderNode;
private final Paint mPaint = new Paint();
private final Path mRectPath = new Path();
private final float[] mTmpRadii = new float[8];
private final SurfaceControl.BlurRegion mBlurRegion = new SurfaceControl.BlurRegion();
- private Aggregator mAggregator;
-
// This will be called from a thread pool.
private final RenderNode.PositionUpdateListener mPositionUpdateListener =
new RenderNode.PositionUpdateListener() {
@Override
public void positionChanged(long frameNumber, int left, int top, int right,
int bottom) {
- if (mAggregator == null) {
+ synchronized (mAggregator) {
mBlurRegion.rect.set(left, top, right, bottom);
- } else {
- synchronized (mAggregator) {
- mBlurRegion.rect.set(left, top, right, bottom);
- mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion);
- }
+ mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion);
}
}
@Override
public void positionLost(long frameNumber) {
- if (mAggregator == null) {
+ synchronized (mAggregator) {
mBlurRegion.rect.setEmpty();
- } else {
- synchronized (mAggregator) {
- mBlurRegion.rect.setEmpty();
- mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion);
- }
+ mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion);
}
}
};
- @RequiresPermission(android.Manifest.permission.USE_BACKGROUND_BLUR)
- public BackgroundBlurDrawable() {
+ private BackgroundBlurDrawable(Aggregator aggregator) {
+ mAggregator = aggregator;
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
mPaint.setColor(Color.TRANSPARENT);
mRenderNode = new RenderNode("BackgroundBlurDrawable");
mRenderNode.addPositionUpdateListener(mPositionUpdateListener);
}
- /**
- * @hide
- */
@Override
public void draw(@NonNull Canvas canvas) {
if (mRectPath.isEmpty() || !isVisible() || getAlpha() == 0) {
@@ -113,9 +100,6 @@
mPaint.setColor(color);
}
- /**
- * @hide
- */
@Override
public boolean setVisible(boolean visible, boolean restart) {
boolean changed = super.setVisible(visible, restart);
@@ -125,9 +109,6 @@
return changed;
}
- /**
- * @hide
- */
@Override
public void setAlpha(int alpha) {
mBlurRegion.alpha = alpha / 255f;
@@ -158,12 +139,12 @@
*/
public void setCornerRadius(float cornerRadiusTL, float cornerRadiusTR, float cornerRadiusBL,
float cornerRadiusBR) {
- maybeRunSynchronized(() -> {
+ synchronized (mAggregator) {
mBlurRegion.cornerRadiusTL = cornerRadiusTL;
mBlurRegion.cornerRadiusTR = cornerRadiusTR;
mBlurRegion.cornerRadiusBL = cornerRadiusBL;
mBlurRegion.cornerRadiusBR = cornerRadiusBR;
- });
+ }
updatePath();
invalidateSelf();
}
@@ -176,13 +157,12 @@
}
private void updatePath() {
- maybeRunSynchronized(() -> {
+ synchronized (mAggregator) {
mTmpRadii[0] = mTmpRadii[1] = mBlurRegion.cornerRadiusTL;
mTmpRadii[2] = mTmpRadii[3] = mBlurRegion.cornerRadiusTR;
mTmpRadii[4] = mTmpRadii[5] = mBlurRegion.cornerRadiusBL;
mTmpRadii[6] = mTmpRadii[7] = mBlurRegion.cornerRadiusBR;
- });
-
+ }
mRectPath.reset();
if (getAlpha() == 0 || !isVisible()) {
return;
@@ -192,62 +172,19 @@
Path.Direction.CW);
}
- /**
- * @hide
- */
@Override
public void setColorFilter(@Nullable ColorFilter colorFilter) {
throw new IllegalArgumentException("not implemented");
}
- /**
- * @hide
- */
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
/**
- * @hide
- */
- @Override
- public void onAttached(@NonNull View v) {
- super.onAttached(v);
- mAggregator = v.getViewRootImpl().getBlurRegionAggregator();
- }
-
- /**
- * @hide
- */
- @Override
- public void onDetached(@NonNull View v) {
- super.onDetached(v);
- mAggregator = null;
- }
-
- /**
- * The Aggregator is called from the RenderThread to aggregate all blur regions and send them
- * to SurfaceFlinger. Since the BackgroundBlurDrawable could be updated at any time from the
- * main thread, we need to synchronize the two threads. The BackgroundBlurDrawable may be
- * instantiated before the ViewRootImpl is created, i.e. before the Aggregator is created.
- * In that case, updates are not synchronized.
- */
- private void maybeRunSynchronized(Runnable r) {
- if (mAggregator == null) {
- r.run();
- } else {
- synchronized (mAggregator) {
- r.run();
- }
- }
- }
-
- /**
* Responsible for keeping track of all blur regions of a {@link ViewRootImpl} and posting a
* message when it's time to propagate them.
- *
- * @hide
*/
public static final class Aggregator {
@@ -262,6 +199,16 @@
}
/**
+ * Creates a blur region with default radius.
+ */
+ public BackgroundBlurDrawable createBackgroundBlurDrawable(Context context) {
+ BackgroundBlurDrawable drawable = new BackgroundBlurDrawable(this);
+ drawable.setBlurRadius(context.getResources().getDimensionPixelSize(
+ R.dimen.default_background_blur_radius));
+ return drawable;
+ }
+
+ /**
* Called from RenderThread only, already locked.
* @param drawable
* @param blurRegion
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 3be841c..141dc79 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2540,14 +2540,8 @@
}
}
- if (a.getBoolean(R.styleable.Window_windowBackgroundBlurEnabled, false)) {
- if ((getForcedWindowFlags() & WindowManager.LayoutParams.FLAG_BLUR_BEHIND) == 0) {
- params.flags |= WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
- }
-
- params.backgroundBlurRadius = a.getDimensionPixelSize(
- android.R.styleable.Window_windowBackgroundBlurRadius, 0);
- }
+ params.backgroundBlurRadius = a.getDimensionPixelSize(
+ R.styleable.Window_windowBackgroundBlurRadius, 0);
if (params.windowAnimations == 0) {
params.windowAnimations = a.getResourceId(
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7c1e1b5f..989e25d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4133,11 +4133,6 @@
<permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_MODE"
android:protectionLevel="signature" />
- <!-- @SystemApi Allows an application to use background blur.
- @hide -->
- <permission android:name="android.permission.USE_BACKGROUND_BLUR"
- android:protectionLevel="signature|privileged" />
-
<!-- Allows an application to control the lights on the device.
@hide
@SystemApi
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 07c3adf..26fb8b8 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -87,16 +87,6 @@
theme does not set this value, meaning it is based on whether the
window is floating. -->
<attr name="backgroundDimEnabled" format="boolean" />
- <!-- When windowBackgroundBlurEnabled is set, this is the amount of blur to apply
- behind the window. The range is from 0, which means no blur, to 150.
- @hide @SystemApi -->
- <attr name="windowBackgroundBlurRadius" format="dimension"/>
- <!-- If set, the area behind the window will be blurred with radius
- windowBackgroundBlurRadius.
- @hide @SystemApi -->
- <attr name="windowBackgroundBlurEnabled" format="boolean" />
-
-
<!-- Color of background imagery used for popup windows. -->
<attr name="colorPopupBackground" format="color" />
<!-- Color used for list divider. -->
@@ -1974,8 +1964,6 @@
<attr name="textColor" />
<attr name="backgroundDimEnabled" />
<attr name="backgroundDimAmount" />
- <attr name="windowBackgroundBlurEnabled" />
- <attr name="windowBackgroundBlurRadius" />
<attr name="windowActionBar" />
<attr name="windowActionModeOverlay" />
<attr name="windowActionBarOverlay" />
@@ -2193,6 +2181,10 @@
the decor view. -->
<attr name="windowLightNavigationBar" format="boolean" />
+ <!-- @hide -->
+ <attr name="windowBackgroundBlurRadius" format="dimension"/>
+
+
<!-- Controls how the window is laid out if there is a {@code DisplayCutout}.
<p>
Defaults to {@code default}.
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b76ab3a..2381fc6 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3049,10 +3049,7 @@
<public name="windowLayoutAffinity" />
<public name="canPauseRecording" />
<!-- @hide -->
- <!-- @hide @SystemApi -->
<public name="windowBackgroundBlurRadius"/>
- <!-- @hide @SystemApi -->
- <public name="windowBackgroundBlurEnabled"/>
<public name="requireDeviceScreenOn" />
<public name="pathSuffix" />
<public name="sspSuffix" />
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
new file mode 100644
index 0000000..af77b6c
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.os.Parcel;
+
+import org.junit.Test;
+
+public class GenericDocumentTest {
+ @Test
+ public void testRecreateFromParcel() {
+ GenericDocument inDoc =
+ new GenericDocument.Builder<>("uri1", "schema1")
+ .setScore(42)
+ .setPropertyString("propString", "Hello")
+ .setPropertyBytes("propBytes", new byte[][] {{1, 2}})
+ .setPropertyDocument(
+ "propDocument",
+ new GenericDocument.Builder<>("uri2", "schema2")
+ .setPropertyString("propString", "Goodbye")
+ .setPropertyBytes("propBytes", new byte[][] {{3, 4}})
+ .build())
+ .build();
+
+ // Serialize the document
+ Parcel inParcel = Parcel.obtain();
+ inParcel.writeBundle(inDoc.getBundle());
+ byte[] data = inParcel.marshall();
+ inParcel.recycle();
+
+ // Deserialize the document
+ Parcel outParcel = Parcel.obtain();
+ outParcel.unmarshall(data, 0, data.length);
+ outParcel.setDataPosition(0);
+ Bundle outBundle = outParcel.readBundle();
+ outParcel.recycle();
+
+ // Compare results
+ GenericDocument outDoc = new GenericDocument(outBundle);
+ assertThat(inDoc).isEqualTo(outDoc);
+ assertThat(outDoc.getPropertyString("propString")).isEqualTo("Hello");
+ assertThat(outDoc.getPropertyBytesArray("propBytes")).isEqualTo(new byte[][] {{1, 2}});
+ assertThat(outDoc.getPropertyDocument("propDocument").getPropertyString("propString"))
+ .isEqualTo("Goodbye");
+ assertThat(outDoc.getPropertyDocument("propDocument").getPropertyBytesArray("propBytes"))
+ .isEqualTo(new byte[][] {{3, 4}});
+ }
+}
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 986079f..7d175d9 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
@@ -34,9 +34,9 @@
new AppSearchEmail.Builder("test1").build(),
new AppSearchEmail.Builder("test2").build());
PutDocumentsRequest request =
- new PutDocumentsRequest.Builder().addGenericDocument(emails).build();
+ new PutDocumentsRequest.Builder().addGenericDocuments(emails).build();
- assertThat(request.getDocuments().get(0).getUri()).isEqualTo("test1");
- assertThat(request.getDocuments().get(1).getUri()).isEqualTo("test2");
+ assertThat(request.getGenericDocuments().get(0).getUri()).isEqualTo("test1");
+ assertThat(request.getGenericDocuments().get(1).getUri()).isEqualTo("test2");
}
}
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
index c8cee85..0fe44630 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
@@ -33,8 +33,8 @@
SearchSpec searchSpec =
new SearchSpec.Builder()
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
- .addNamespace("namespace1", "namespace2")
- .addSchemaType("schemaTypes1", "schemaTypes2")
+ .addFilterNamespaces("namespace1", "namespace2")
+ .addFilterSchemas("schemaTypes1", "schemaTypes2")
.addFilterPackageNames("package1", "package2")
.setSnippetCount(5)
.setSnippetCountPerProperty(10)
@@ -49,7 +49,7 @@
.isEqualTo(SearchSpec.TERM_MATCH_PREFIX);
assertThat(bundle.getStringArrayList(SearchSpec.NAMESPACE_FIELD))
.containsExactly("namespace1", "namespace2");
- assertThat(bundle.getStringArrayList(SearchSpec.SCHEMA_TYPE_FIELD))
+ assertThat(bundle.getStringArrayList(SearchSpec.SCHEMA_FIELD))
.containsExactly("schemaTypes1", "schemaTypes2");
assertThat(bundle.getStringArrayList(SearchSpec.PACKAGE_NAME_FIELD))
.containsExactly("package1", "package2");
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java
index aab9229..bf6b07f 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java
@@ -75,12 +75,12 @@
AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
// By default, the schema is visible.
- SetSchemaRequest request = new SetSchemaRequest.Builder().addSchema(schema).build();
+ SetSchemaRequest request = new SetSchemaRequest.Builder().addSchemas(schema).build();
assertThat(request.getSchemasNotVisibleToSystemUi()).isEmpty();
request =
new SetSchemaRequest.Builder()
- .addSchema(schema)
+ .addSchemas(schema)
.setSchemaTypeVisibilityForSystemUi("Schema", true)
.build();
assertThat(request.getSchemasNotVisibleToSystemUi()).isEmpty();
@@ -91,7 +91,7 @@
AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
SetSchemaRequest request =
new SetSchemaRequest.Builder()
- .addSchema(schema)
+ .addSchemas(schema)
.setSchemaTypeVisibilityForSystemUi("Schema", false)
.build();
assertThat(request.getSchemasNotVisibleToSystemUi()).containsExactly("Schema");
@@ -102,7 +102,7 @@
AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
// By default, the schema is not visible.
- SetSchemaRequest request = new SetSchemaRequest.Builder().addSchema(schema).build();
+ SetSchemaRequest request = new SetSchemaRequest.Builder().addSchemas(schema).build();
assertThat(request.getSchemasVisibleToPackages()).isEmpty();
PackageIdentifier packageIdentifier =
@@ -112,7 +112,7 @@
request =
new SetSchemaRequest.Builder()
- .addSchema(schema)
+ .addSchemas(schema)
.setSchemaTypeVisibilityForPackage(
"Schema", /*visible=*/ true, packageIdentifier)
.build();
@@ -126,7 +126,7 @@
SetSchemaRequest request =
new SetSchemaRequest.Builder()
- .addSchema(schema)
+ .addSchemas(schema)
.setSchemaTypeVisibilityForPackage(
"Schema",
/*visible=*/ false,
@@ -147,7 +147,7 @@
SetSchemaRequest request =
new SetSchemaRequest.Builder()
- .addSchema(schema)
+ .addSchemas(schema)
// Set it visible for "Schema"
.setSchemaTypeVisibilityForPackage(
"Schema", /*visible=*/ true, packageIdentifier)
@@ -165,7 +165,7 @@
SetSchemaRequest request =
new SetSchemaRequest.Builder()
- .addSchema(schema)
+ .addSchemas(schema)
// First set it as visible
.setSchemaTypeVisibilityForPackage(
"Schema",
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 88faa0a..57c0be5 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -78,6 +78,7 @@
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -334,7 +335,8 @@
private ParsingPackage parsePackage(Uri packageURI) {
final String archiveFilePath = packageURI.getPath();
ParseResult<ParsingPackage> result = ParsingPackageUtils.parseDefaultOneTime(
- new File(archiveFilePath), 0 /*flags*/, false /*collectCertificates*/);
+ new File(archiveFilePath), 0 /*flags*/, Collections.emptyList(),
+ false /*collectCertificates*/);
if (result.isError()) {
throw new IllegalStateException(result.getErrorMessage(), result.getException());
}
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 7f22dc2..28b3b04 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -1465,24 +1465,6 @@
}
/**
- * Notifies the drawable that it has been attached.
- *
- * @param v The view that it is attached to
- * @hide
- */
- public void onAttached(View v) {
- }
-
- /**
- * Notifies the drawable that it has been detached.
- *
- * @param v The view that it is detached from
- * @hide
- */
- public void onDetached(View v) {
- }
-
- /**
* Sets the source override density for this Drawable. If non-zero, this density is to be used
* for any calls to {@link Resources#getDrawableForDensity(int, int, Theme)} or
* {@link Resources#getValueForDensity(int, int, TypedValue, boolean)}.
diff --git a/packages/SystemUI/res/drawable/btn_restart.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/btn_restart.xml
rename to libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
diff --git a/libs/WindowManager/Shell/res/layout/pip_menu.xml b/libs/WindowManager/Shell/res/layout/pip_menu.xml
index b581f55..93a6e7b 100644
--- a/libs/WindowManager/Shell/res/layout/pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/pip_menu.xml
@@ -97,4 +97,14 @@
android:padding="@dimen/pip_resize_handle_padding"
android:src="@drawable/pip_resize_handle"
android:background="?android:selectableItemBackgroundBorderless" />
-</FrameLayout>
+
+ <!-- invisible layer to trap the focus, b/169372603 -->
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@null"
+ android:defaultFocusHighlightEnabled="false"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:focusedByDefault="true" />
+t </FrameLayout>
diff --git a/packages/SystemUI/res/layout/size_compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
similarity index 100%
rename from packages/SystemUI/res/layout/size_compat_mode_hint.xml
rename to libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 30ef72c..b1425e4 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -146,4 +146,10 @@
<!-- Content description to tell the user a bubble has been dismissed. -->
<string name="accessibility_bubble_dismissed">Bubble dismissed.</string>
+
+ <!-- Description of the restart button in the hint of size compatibility mode. [CHAR LIMIT=NONE] -->
+ <string name="restart_button_description">Tap to restart this app and go full screen.</string>
+
+ <!-- Generic "got it" acceptance of dialog or cling [CHAR LIMIT=NONE] -->
+ <string name="got_it">Got it</string>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
index 4b3fc81..afe523a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
@@ -106,5 +106,4 @@
public String toString() {
return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_FULLSCREEN);
}
-
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index c9b38d0..a570c0a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -44,6 +44,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.sizecompatui.SizeCompatUI;
import com.android.wm.shell.startingsurface.StartingSurfaceDrawer;
import java.io.PrintWriter;
@@ -102,18 +103,31 @@
private final Object mLock = new Object();
private final StartingSurfaceDrawer mStartingSurfaceDrawer;
+ /**
+ * In charge of showing size compat UI. Can be {@code null} if device doesn't support size
+ * compat.
+ */
+ @Nullable
+ private final SizeCompatUI mSizeCompatUI;
+
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) {
- this(null, mainExecutor, context);
+ this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */);
+ }
+
+ public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
+ SizeCompatUI sizeCompatUI) {
+ this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI);
}
@VisibleForTesting
ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController, ShellExecutor mainExecutor,
- Context context) {
+ Context context, @Nullable SizeCompatUI sizeCompatUI) {
super(taskOrganizerController, mainExecutor);
// TODO(b/131727939) temporarily live here, the starting surface drawer should be controlled
// by a controller, that class should be create while porting
// ActivityRecord#addStartingWindow to WMShell.
mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, mainExecutor);
+ mSizeCompatUI = sizeCompatUI;
}
@Override
@@ -255,6 +269,7 @@
if (listener != null) {
listener.onTaskAppeared(info.getTaskInfo(), info.getLeash());
}
+ notifySizeCompatUI(info.getTaskInfo(), listener);
}
@Override
@@ -270,6 +285,10 @@
if (!updated && newListener != null) {
newListener.onTaskInfoChanged(taskInfo);
}
+ if (updated || !taskInfo.equalsForSizeCompat(data.getTaskInfo())) {
+ // Notify the size compat UI if the listener or task info changed.
+ notifySizeCompatUI(taskInfo, newListener);
+ }
}
}
@@ -294,6 +313,8 @@
if (listener != null) {
listener.onTaskVanished(taskInfo);
}
+ // Pass null for listener to remove the size compat UI on this task if there is any.
+ notifySizeCompatUI(taskInfo, null /* taskListener */);
}
}
@@ -320,6 +341,34 @@
return true;
}
+ /**
+ * Notifies {@link SizeCompatUI} about the size compat info changed on the give Task to update
+ * the UI accordingly.
+ *
+ * @param taskInfo the new Task info
+ * @param taskListener listener to handle the Task Surface placement. {@code null} if task is
+ * vanished.
+ */
+ private void notifySizeCompatUI(RunningTaskInfo taskInfo, @Nullable TaskListener taskListener) {
+ if (mSizeCompatUI == null) {
+ return;
+ }
+
+ // The task is vanished, notify to remove size compat UI on this Task if there is any.
+ if (taskListener == null) {
+ mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
+ null /* taskConfig */, null /* sizeCompatActivity*/,
+ null /* taskListener */);
+ return;
+ }
+
+ mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
+ taskInfo.configuration.windowConfiguration.getBounds(),
+ // null if the top activity not in size compat.
+ taskInfo.topActivityInSizeCompat ? taskInfo.topActivityToken : null,
+ taskListener);
+ }
+
private TaskListener getTaskListener(RunningTaskInfo runningTaskInfo) {
return getTaskListener(runningTaskInfo, false /*removeLaunchCookieIfNeeded*/);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index a2cd683..b2ac61c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -159,6 +159,14 @@
}
}
+ private void dispatchVisibilityChanged(int displayId, boolean isShowing) {
+ synchronized (mPositionProcessors) {
+ for (ImePositionProcessor pp : mPositionProcessors) {
+ pp.onImeVisibilityChanged(displayId, isShowing);
+ }
+ }
+ }
+
/**
* Adds an {@link ImePositionProcessor} to be called during ime position updates.
*/
@@ -212,7 +220,7 @@
return;
}
- mImeShowing = insetsState.getSourceOrDefaultVisibility(InsetsState.ITYPE_IME);
+ updateImeVisibility(insetsState.getSourceOrDefaultVisibility(InsetsState.ITYPE_IME));
final InsetsSource newSource = insetsState.getSource(InsetsState.ITYPE_IME);
final Rect newFrame = newSource.getFrame();
@@ -371,7 +379,7 @@
seek = true;
}
mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE;
- mImeShowing = show;
+ updateImeVisibility(show);
mAnimation = ValueAnimator.ofFloat(startY, endY);
mAnimation.setDuration(
show ? ANIMATION_DURATION_SHOW_MS : ANIMATION_DURATION_HIDE_MS);
@@ -455,6 +463,13 @@
}
}
+ private void updateImeVisibility(boolean isShowing) {
+ if (mImeShowing != isShowing) {
+ mImeShowing = isShowing;
+ dispatchVisibilityChanged(mDisplayId, isShowing);
+ }
+ }
+
@VisibleForTesting
@BinderThread
public class DisplayWindowInsetsControllerImpl
@@ -563,6 +578,15 @@
default void onImeEndPositioning(int displayId, boolean cancel,
SurfaceControl.Transaction t) {
}
+
+ /**
+ * Called when the IME visibility changed.
+ *
+ * @param isShowing {@code true} if the IME is shown.
+ */
+ default void onImeVisibilityChanged(int displayId, boolean isShowing) {
+
+ }
}
public IInputMethodManager getImms() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
new file mode 100644
index 0000000..e47e1ac
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.sizecompatui;
+
+import android.app.ActivityClient;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.RippleDrawable;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.PopupWindow;
+
+import com.android.wm.shell.R;
+
+/** Button to restart the size compat activity. */
+class SizeCompatRestartButton extends ImageButton implements View.OnClickListener,
+ View.OnLongClickListener {
+ private static final String TAG = "SizeCompatRestartButton";
+
+ final WindowManager.LayoutParams mWinParams;
+ final boolean mShouldShowHint;
+ final int mDisplayId;
+ final int mPopupOffsetX;
+ final int mPopupOffsetY;
+
+ private IBinder mLastActivityToken;
+ private PopupWindow mShowingHint;
+
+ SizeCompatRestartButton(Context context, int displayId, boolean hasShownHint) {
+ super(context);
+ mDisplayId = displayId;
+ mShouldShowHint = !hasShownHint;
+ final Drawable drawable = context.getDrawable(R.drawable.size_compat_restart_button);
+ setImageDrawable(drawable);
+ setContentDescription(context.getString(R.string.restart_button_description));
+
+ final int drawableW = drawable.getIntrinsicWidth();
+ final int drawableH = drawable.getIntrinsicHeight();
+ mPopupOffsetX = drawableW / 2;
+ mPopupOffsetY = drawableH * 2;
+
+ final ColorStateList color = ColorStateList.valueOf(Color.LTGRAY);
+ final GradientDrawable mask = new GradientDrawable();
+ mask.setShape(GradientDrawable.OVAL);
+ mask.setColor(color);
+ setBackground(new RippleDrawable(color, null /* content */, mask));
+ setOnClickListener(this);
+ setOnLongClickListener(this);
+
+ mWinParams = new WindowManager.LayoutParams();
+ mWinParams.gravity = getGravity(getResources().getConfiguration().getLayoutDirection());
+ mWinParams.width = drawableW * 2;
+ mWinParams.height = drawableH * 2;
+ mWinParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+ mWinParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+ mWinParams.format = PixelFormat.TRANSLUCENT;
+ mWinParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ mWinParams.setTitle(SizeCompatRestartButton.class.getSimpleName()
+ + context.getDisplayId());
+ }
+
+ void updateLastTargetActivity(IBinder activityToken) {
+ mLastActivityToken = activityToken;
+ }
+
+ /** @return {@code false} if the target display is invalid. */
+ boolean show() {
+ try {
+ getContext().getSystemService(WindowManager.class).addView(this, mWinParams);
+ } catch (WindowManager.InvalidDisplayException e) {
+ // The target display may have been removed when the callback has just arrived.
+ Log.w(TAG, "Cannot show on display " + getContext().getDisplayId(), e);
+ return false;
+ }
+ return true;
+ }
+
+ void remove() {
+ if (mShowingHint != null) {
+ mShowingHint.dismiss();
+ }
+ getContext().getSystemService(WindowManager.class).removeViewImmediate(this);
+ }
+
+ @Override
+ public void onClick(View v) {
+ ActivityClient.getInstance().restartActivityProcessIfVisible(mLastActivityToken);
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ showHint();
+ return true;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (mShouldShowHint) {
+ showHint();
+ }
+ }
+
+ @Override
+ public void setLayoutDirection(int layoutDirection) {
+ final int gravity = getGravity(layoutDirection);
+ if (mWinParams.gravity != gravity) {
+ mWinParams.gravity = gravity;
+ if (mShowingHint != null) {
+ mShowingHint.dismiss();
+ showHint();
+ }
+ getContext().getSystemService(WindowManager.class).updateViewLayout(this,
+ mWinParams);
+ }
+ super.setLayoutDirection(layoutDirection);
+ }
+
+ void showHint() {
+ if (mShowingHint != null) {
+ return;
+ }
+
+ final View popupView = LayoutInflater.from(getContext()).inflate(
+ R.layout.size_compat_mode_hint, null /* root */);
+ final PopupWindow popupWindow = new PopupWindow(popupView,
+ LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
+ popupWindow.setWindowLayoutType(mWinParams.type);
+ popupWindow.setElevation(getResources().getDimension(R.dimen.bubble_elevation));
+ popupWindow.setAnimationStyle(android.R.style.Animation_InputMethod);
+ popupWindow.setClippingEnabled(false);
+ popupWindow.setOnDismissListener(() -> mShowingHint = null);
+ mShowingHint = popupWindow;
+
+ final Button gotItButton = popupView.findViewById(R.id.got_it);
+ gotItButton.setBackground(new RippleDrawable(ColorStateList.valueOf(Color.LTGRAY),
+ null /* content */, null /* mask */));
+ gotItButton.setOnClickListener(view -> popupWindow.dismiss());
+ popupWindow.showAtLocation(this, mWinParams.gravity, mPopupOffsetX, mPopupOffsetY);
+ }
+
+ private static int getGravity(int layoutDirection) {
+ return Gravity.BOTTOM
+ | (layoutDirection == View.LAYOUT_DIRECTION_RTL ? Gravity.START : Gravity.END);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java
new file mode 100644
index 0000000..11f22ed
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.sizecompatui;
+
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.os.IBinder;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.annotations.ExternalThread;
+
+/**
+ * Interface to engage size compat mode UI.
+ */
+@ExternalThread
+public interface SizeCompatUI {
+ /**
+ * Called when the Task info changed. Creates and updates the restart button if there is an
+ * activity in size compat, or removes the restart button if there is no size compat activity.
+ *
+ * @param displayId display the task and activity are in.
+ * @param taskId task the activity is in.
+ * @param taskBounds task bounds to place the restart button in.
+ * @param sizeCompatActivity the size compat activity in the task. Can be {@code null} if the
+ * top activity in this Task is not in size compat.
+ * @param taskListener listener to handle the Task Surface placement.
+ */
+ void onSizeCompatInfoChanged(int displayId, int taskId, @Nullable Rect taskBounds,
+ @Nullable IBinder sizeCompatActivity,
+ @Nullable ShellTaskOrganizer.TaskListener taskListener);
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
new file mode 100644
index 0000000..8cb16c8
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.sizecompatui;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.os.IBinder;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.View;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.ShellExecutor;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Shows a restart-activity button on Task when the foreground activity is in size compatibility
+ * mode.
+ */
+public class SizeCompatUIController implements DisplayController.OnDisplaysChangedListener,
+ DisplayImeController.ImePositionProcessor {
+ private static final String TAG = "SizeCompatUI";
+
+ /** The showing buttons by task id. */
+ private final SparseArray<SizeCompatRestartButton> mActiveButtons = new SparseArray<>(1);
+ /** Avoid creating display context frequently for non-default display. */
+ private final SparseArray<WeakReference<Context>> mDisplayContextCache = new SparseArray<>(0);
+
+ @VisibleForTesting
+ final SizeCompatUI mImpl = new SizeCompatUIImpl();
+ private final Context mContext;
+ private final ShellExecutor mMainExecutor;
+ private final DisplayController mDisplayController;
+ private final DisplayImeController mImeController;
+
+ /** Only show once automatically in the process life. */
+ private boolean mHasShownHint;
+
+ /** Creates the {@link SizeCompatUIController}. */
+ public static SizeCompatUI create(Context context,
+ DisplayController displayController,
+ DisplayImeController imeController,
+ ShellExecutor mainExecutor) {
+ return new SizeCompatUIController(context, displayController, imeController, mainExecutor)
+ .mImpl;
+ }
+
+ @VisibleForTesting
+ SizeCompatUIController(Context context,
+ DisplayController displayController,
+ DisplayImeController imeController,
+ ShellExecutor mainExecutor) {
+ mContext = context;
+ mMainExecutor = mainExecutor;
+ mDisplayController = displayController;
+ mImeController = imeController;
+ mDisplayController.addDisplayWindowListener(this);
+ mImeController.addPositionProcessor(this);
+ }
+
+ private void onSizeCompatInfoChanged(int displayId, int taskId, @Nullable Rect taskBounds,
+ @Nullable IBinder sizeCompatActivity,
+ @Nullable ShellTaskOrganizer.TaskListener taskListener) {
+ // TODO Draw button on Task surface
+ if (taskBounds == null || sizeCompatActivity == null || taskListener == null) {
+ // Null token means the current foreground activity is not in size compatibility mode.
+ removeRestartButton(taskId);
+ } else {
+ updateRestartButton(displayId, taskId, sizeCompatActivity);
+ }
+ }
+
+ // TODO move from SizeCompatModeActivityController from system UI.
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ mDisplayContextCache.remove(displayId);
+ for (int i = 0; i < mActiveButtons.size(); i++) {
+ final int taskId = mActiveButtons.keyAt(i);
+ final SizeCompatRestartButton button = mActiveButtons.get(taskId);
+ if (button != null && button.mDisplayId == displayId) {
+ removeRestartButton(taskId);
+ }
+ }
+ }
+
+ @Override
+ public void onImeVisibilityChanged(int displayId, boolean isShowing) {
+ final int newVisibility = isShowing ? View.GONE : View.VISIBLE;
+ for (int i = 0; i < mActiveButtons.size(); i++) {
+ final int taskId = mActiveButtons.keyAt(i);
+ final SizeCompatRestartButton button = mActiveButtons.get(taskId);
+ if (button == null || button.mDisplayId != displayId) {
+ continue;
+ }
+
+ // Hide the button when input method is showing.
+ if (button.getVisibility() != newVisibility) {
+ button.setVisibility(newVisibility);
+ }
+ }
+ }
+
+ private void updateRestartButton(int displayId, int taskId, IBinder activityToken) {
+ SizeCompatRestartButton restartButton = mActiveButtons.get(taskId);
+ if (restartButton != null) {
+ restartButton.updateLastTargetActivity(activityToken);
+ return;
+ }
+
+ final Context context = getOrCreateDisplayContext(displayId);
+ if (context == null) {
+ Log.i(TAG, "Cannot get context for display " + displayId);
+ return;
+ }
+
+ restartButton = createRestartButton(context, displayId);
+ restartButton.updateLastTargetActivity(activityToken);
+ if (restartButton.show()) {
+ mActiveButtons.append(taskId, restartButton);
+ } else {
+ onDisplayRemoved(displayId);
+ }
+ }
+
+ @VisibleForTesting
+ SizeCompatRestartButton createRestartButton(Context context, int displayId) {
+ final SizeCompatRestartButton button = new SizeCompatRestartButton(context, displayId,
+ mHasShownHint);
+ // Only show hint for the first time.
+ mHasShownHint = true;
+ return button;
+ }
+
+ private void removeRestartButton(int taskId) {
+ final SizeCompatRestartButton button = mActiveButtons.get(taskId);
+ if (button != null) {
+ button.remove();
+ mActiveButtons.remove(taskId);
+ }
+ }
+
+ private Context getOrCreateDisplayContext(int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ return mContext;
+ }
+ Context context = null;
+ final WeakReference<Context> ref = mDisplayContextCache.get(displayId);
+ if (ref != null) {
+ context = ref.get();
+ }
+ if (context == null) {
+ Display display = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
+ if (display != null) {
+ context = mContext.createDisplayContext(display);
+ mDisplayContextCache.put(displayId, new WeakReference<>(context));
+ }
+ }
+ return context;
+ }
+
+ private class SizeCompatUIImpl implements SizeCompatUI {
+ @Override
+ public void onSizeCompatInfoChanged(int displayId, int taskId, @Nullable Rect taskBounds,
+ @Nullable IBinder sizeCompatActivity,
+ @Nullable ShellTaskOrganizer.TaskListener taskListener) {
+ mMainExecutor.execute(() ->
+ SizeCompatUIController.this.onSizeCompatInfoChanged(displayId, taskId,
+ taskBounds, sizeCompatActivity, taskListener));
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 862776e..80ea9b9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -16,11 +16,14 @@
package com.android.wm.shell;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_MULTI_WINDOW;
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP;
@@ -48,6 +51,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.sizecompatui.SizeCompatUI;
import org.junit.Before;
import org.junit.Test;
@@ -59,6 +63,9 @@
/**
* Tests for the shell task organizer.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:ShellTaskOrganizerTests
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -68,6 +75,8 @@
private ITaskOrganizerController mTaskOrganizerController;
@Mock
private Context mContext;
+ @Mock
+ private SizeCompatUI mSizeCompatUI;
ShellTaskOrganizer mOrganizer;
private final SyncTransactionQueue mSyncTransactionQueue = mock(SyncTransactionQueue.class);
@@ -102,7 +111,8 @@
doReturn(ParceledListSlice.<TaskAppearedInfo>emptyList())
.when(mTaskOrganizerController).registerTaskOrganizer(any());
} catch (RemoteException e) {}
- mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mTestExecutor, mContext));
+ mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mTestExecutor, mContext,
+ mSizeCompatUI));
}
@Test
@@ -257,6 +267,39 @@
assertTrue(mwListener.appeared.contains(task2));
}
+ @Test
+ public void testOnSizeCompatActivityChanged() {
+ final RunningTaskInfo taskInfo1 = createTaskInfo(12, WINDOWING_MODE_FULLSCREEN);
+ taskInfo1.displayId = DEFAULT_DISPLAY;
+ taskInfo1.topActivityToken = mock(IBinder.class);
+ taskInfo1.topActivityInSizeCompat = false;
+ final TrackingTaskListener taskListener = new TrackingTaskListener();
+ mOrganizer.addListenerForType(taskListener, TASK_LISTENER_TYPE_FULLSCREEN);
+ mOrganizer.onTaskAppeared(taskInfo1, null);
+
+ // sizeCompatActivity is null if top activity is not in size compat.
+ verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
+ taskInfo1.configuration.windowConfiguration.getBounds(),
+ null /* sizeCompatActivity*/ , taskListener);
+
+ // sizeCompatActivity is non-null if top activity is in size compat.
+ final RunningTaskInfo taskInfo2 =
+ createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
+ taskInfo2.displayId = taskInfo1.displayId;
+ taskInfo2.topActivityToken = taskInfo1.topActivityToken;
+ taskInfo2.topActivityInSizeCompat = true;
+ mOrganizer.onTaskInfoChanged(taskInfo2);
+ verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
+ taskInfo1.configuration.windowConfiguration.getBounds(),
+ taskInfo1.topActivityToken,
+ taskListener);
+
+ mOrganizer.onTaskVanished(taskInfo1);
+ verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
+ null /* taskConfig */, null /* sizeCompatActivity*/,
+ null /* taskListener */);
+ }
+
private static RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
RunningTaskInfo taskInfo = new RunningTaskInfo();
taskInfo.taskId = taskId;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
new file mode 100644
index 0000000..98f01ff
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
@@ -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.wm.shell.sizecompatui;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link SizeCompatUIController}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:SizeCompatUIControllerTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class SizeCompatUIControllerTest extends ShellTestCase {
+ private static final int DISPLAY_ID = 0;
+
+ private final TestShellExecutor mShellMainExecutor = new TestShellExecutor();
+
+ private SizeCompatUIController mController;
+ private @Mock DisplayController mMockDisplayController;
+ private @Mock DisplayImeController mMockImeController;
+ private @Mock SizeCompatRestartButton mMockButton;
+ private @Mock IBinder mMockActivityToken;
+ private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ doReturn(true).when(mMockButton).show();
+
+ mController = new SizeCompatUIController(mContext, mMockDisplayController,
+ mMockImeController, mShellMainExecutor) {
+ @Override
+ SizeCompatRestartButton createRestartButton(Context context, int displayId) {
+ return mMockButton;
+ }
+ };
+ }
+
+ @Test
+ public void testListenerRegistered() {
+ verify(mMockDisplayController).addDisplayWindowListener(mController);
+ verify(mMockImeController).addPositionProcessor(mController);
+ }
+
+ @Test
+ public void testOnSizeCompatInfoChanged() {
+ final int taskId = 12;
+ final Rect taskBounds = new Rect(0, 0, 1000, 2000);
+
+ // Verify that the restart button is added with non-null size compat activity.
+ mController.mImpl.onSizeCompatInfoChanged(DISPLAY_ID, taskId, taskBounds,
+ mMockActivityToken, mMockTaskListener);
+ mShellMainExecutor.flushAll();
+
+ verify(mMockButton).show();
+ verify(mMockButton).updateLastTargetActivity(eq(mMockActivityToken));
+
+ // Verify that the restart button is removed with null size compat activity.
+ mController.mImpl.onSizeCompatInfoChanged(DISPLAY_ID, taskId, null, null, null);
+
+ mShellMainExecutor.flushAll();
+ verify(mMockButton).remove();
+ }
+
+ @Test
+ public void testChangeButtonVisibilityOnImeShowHide() {
+ final int taskId = 12;
+ final Rect taskBounds = new Rect(0, 0, 1000, 2000);
+ mController.mImpl.onSizeCompatInfoChanged(DISPLAY_ID, taskId, taskBounds,
+ mMockActivityToken, mMockTaskListener);
+ mShellMainExecutor.flushAll();
+
+ // Verify that the restart button is hidden when IME is visible.
+ doReturn(View.VISIBLE).when(mMockButton).getVisibility();
+ mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
+
+ verify(mMockButton).setVisibility(eq(View.GONE));
+
+ // Verify that the restart button is visible when IME is hidden.
+ doReturn(View.GONE).when(mMockButton).getVisibility();
+ mController.onImeVisibilityChanged(DISPLAY_ID, false /* isShowing */);
+
+ verify(mMockButton).setVisibility(eq(View.VISIBLE));
+ }
+}
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index 65a0110..3dcaffb 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -1380,10 +1380,9 @@
* Gets the Automatic Gain Control level in dB.
*
* <p> AGC acts as a variable gain amplifier adjusting the power of the incoming signal. The AGC
- * level may be used to indicate potential interference. When AGC is at a nominal level, this
- * value must be set as 0. Higher gain (and/or lower input power) shall be output as a positive
- * number. Hence in cases of strong jamming, in the band of this signal, this value will go more
- * negative.
+ * level may be used to indicate potential interference. Higher gain (and/or lower input power)
+ * shall be output as a positive number. Hence in cases of strong jamming, in the band of this
+ * signal, this value will go more negative.
*
* <p> Note: Different hardware designs (e.g. antenna, pre-amplification, or other RF HW
* components) may also affect the typical output of of this value on any given hardware design
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 9657b25e..4d7ed11 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1089,7 +1089,7 @@
private int[] mSampleRates;
private Range<Integer>[] mSampleRateRanges;
- private int mMaxInputChannelCount;
+ private Range<Integer>[] mInputChannelRanges;
private static final int MAX_INPUT_CHANNEL_COUNT = 30;
@@ -1119,11 +1119,61 @@
}
/**
- * Returns the maximum number of input channels supported. The codec
- * supports any number of channels between 1 and this maximum value.
+ * Returns the maximum number of input channels supported.
+ *
+ * Through {@link android.os.Build.VERSION_CODES#R}, this method indicated support
+ * for any number of input channels between 1 and this maximum value.
+ *
+ * As of {@link android.os.Build.VERSION_CODES#S},
+ * the implied lower limit of 1 channel is no longer valid.
+ * As of {@link android.os.Build.VERSION_CODES#S}, {@link #getMaxInputChannelCount} is
+ * superseded by {@link #getInputChannelCountRanges},
+ * which returns an array of ranges of channels.
+ * The {@link #getMaxInputChannelCount} method will return the highest value
+ * in the ranges returned by {@link #getInputChannelCountRanges}
+ *
*/
public int getMaxInputChannelCount() {
- return mMaxInputChannelCount;
+ int overall_max = 0;
+ for (int i = mInputChannelRanges.length - 1; i >= 0; i--) {
+ int lmax = mInputChannelRanges[i].getUpper();
+ if (lmax > overall_max) {
+ overall_max = lmax;
+ }
+ }
+ return overall_max;
+ }
+
+ /**
+ * Returns the minimum number of input channels supported.
+ * This is often 1, but does vary for certain mime types.
+ *
+ * This returns the lowest channel count in the ranges returned by
+ * {@link #getInputChannelCountRanges}.
+ */
+ public int getMinInputChannelCount() {
+ int overall_min = MAX_INPUT_CHANNEL_COUNT;
+ for (int i = mInputChannelRanges.length - 1; i >= 0; i--) {
+ int lmin = mInputChannelRanges[i].getLower();
+ if (lmin < overall_min) {
+ overall_min = lmin;
+ }
+ }
+ return overall_min;
+ }
+
+ /*
+ * Returns an array of ranges representing the number of input channels supported.
+ * The codec supports any number of input channels within this range.
+ *
+ * This supersedes the {@link #getMaxInputChannelCount} method.
+ *
+ * For many codecs, this will be a single range [1..N], for some N.
+ */
+ @SuppressLint("ArrayReturn")
+ @NonNull
+ public Range<Integer>[] getInputChannelCountRanges() {
+ return Arrays.copyOf(mInputChannelRanges, mInputChannelRanges.length);
}
/* no public constructor */
@@ -1146,7 +1196,7 @@
private void initWithPlatformLimits() {
mBitrateRange = Range.create(0, Integer.MAX_VALUE);
- mMaxInputChannelCount = MAX_INPUT_CHANNEL_COUNT;
+ mInputChannelRanges = new Range[] {Range.create(1, MAX_INPUT_CHANNEL_COUNT)};
// mBitrateRange = Range.create(1, 320000);
final int minSampleRate = SystemProperties.
getInt("ro.mediacodec.min_sample_rate", 7350);
@@ -1158,9 +1208,12 @@
private boolean supports(Integer sampleRate, Integer inputChannels) {
// channels and sample rates are checked orthogonally
- if (inputChannels != null &&
- (inputChannels < 1 || inputChannels > mMaxInputChannelCount)) {
- return false;
+ if (inputChannels != null) {
+ int ix = Utils.binarySearchDistinctRanges(
+ mInputChannelRanges, inputChannels);
+ if (ix < 0) {
+ return false;
+ }
}
if (sampleRate != null) {
int ix = Utils.binarySearchDistinctRanges(
@@ -1292,12 +1345,28 @@
} else if (sampleRateRange != null) {
limitSampleRates(new Range[] { sampleRateRange });
}
- applyLimits(maxChannels, bitRates);
+
+ Range<Integer> channelRange = Range.create(1, maxChannels);
+
+ applyLimits(new Range[] { channelRange }, bitRates);
}
- private void applyLimits(int maxInputChannels, Range<Integer> bitRates) {
- mMaxInputChannelCount = Range.create(1, mMaxInputChannelCount)
- .clamp(maxInputChannels);
+ private void applyLimits(Range<Integer>[] inputChannels, Range<Integer> bitRates) {
+
+ // clamp & make a local copy
+ Range<Integer>[] myInputChannels = new Range[inputChannels.length];
+ for (int i = 0; i < inputChannels.length; i++) {
+ int lower = inputChannels[i].clamp(1);
+ int upper = inputChannels[i].clamp(MAX_INPUT_CHANNEL_COUNT);
+ myInputChannels[i] = Range.create(lower, upper);
+ }
+
+ // sort, intersect with existing, & save channel list
+ sortDistinctRanges(myInputChannels);
+ Range<Integer>[] joinedChannelList =
+ intersectSortedDistinctRanges(myInputChannels, mInputChannelRanges);
+ mInputChannelRanges = joinedChannelList;
+
if (bitRates != null) {
mBitrateRange = mBitrateRange.intersect(bitRates);
}
@@ -1305,6 +1374,7 @@
private void parseFromInfo(MediaFormat info) {
int maxInputChannels = MAX_INPUT_CHANNEL_COUNT;
+ Range<Integer>[] channels = new Range[] { Range.create(1, maxInputChannels)};
Range<Integer> bitRates = POSITIVE_INTEGERS;
if (info.containsKey("sample-rate-ranges")) {
@@ -1315,17 +1385,38 @@
}
limitSampleRates(rateRanges);
}
- if (info.containsKey("max-channel-count")) {
+
+ // we will prefer channel-ranges over max-channel-count
+ if (info.containsKey("channel-ranges")) {
+ String[] channelStrings = info.getString("channel-ranges").split(",");
+ Range<Integer>[] channelRanges = new Range[channelStrings.length];
+ for (int i = 0; i < channelStrings.length; i++) {
+ channelRanges[i] = Utils.parseIntRange(channelStrings[i], null);
+ }
+ channels = channelRanges;
+ } else if (info.containsKey("channel-range")) {
+ Range<Integer> oneRange = Utils.parseIntRange(info.getString("channel-range"),
+ null);
+ channels = new Range[] { oneRange };
+ } else if (info.containsKey("max-channel-count")) {
maxInputChannels = Utils.parseIntSafely(
info.getString("max-channel-count"), maxInputChannels);
+ if (maxInputChannels == 0) {
+ channels = new Range[] {Range.create(0, 0)};
+ } else {
+ channels = new Range[] {Range.create(1, maxInputChannels)};
+ }
} else if ((mParent.mError & ERROR_UNSUPPORTED) != 0) {
maxInputChannels = 0;
+ channels = new Range[] {Range.create(0, 0)};
}
+
if (info.containsKey("bitrate-range")) {
bitRates = bitRates.intersect(
Utils.parseIntRange(info.getString("bitrate-range"), bitRates));
}
- applyLimits(maxInputChannels, bitRates);
+
+ applyLimits(channels, bitRates);
}
/** @hide */
@@ -1334,7 +1425,7 @@
if (mBitrateRange.getLower().equals(mBitrateRange.getUpper())) {
format.setInteger(MediaFormat.KEY_BIT_RATE, mBitrateRange.getLower());
}
- if (mMaxInputChannelCount == 1) {
+ if (getMaxInputChannelCount() == 1) {
// mono-only format
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
}
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index 20b16d7..6b78817 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -22,17 +22,24 @@
#include "FilterClient.h"
-using ::aidl::android::media::tv::tuner::TunerFilterAvSettings;
+using ::aidl::android::media::tv::tuner::TunerDemuxIpAddressSettings;
+using ::aidl::android::media::tv::tuner::TunerFilterAlpConfiguration;
+using ::aidl::android::media::tv::tuner::TunerFilterIpConfiguration;
+using ::aidl::android::media::tv::tuner::TunerFilterMmtpConfiguration;
using ::aidl::android::media::tv::tuner::TunerFilterMonitorEvent;
using ::aidl::android::media::tv::tuner::TunerFilterScIndexMask;
+using ::aidl::android::media::tv::tuner::TunerFilterSectionBits;
+using ::aidl::android::media::tv::tuner::TunerFilterSectionCondition;
+using ::aidl::android::media::tv::tuner::TunerFilterSectionTableInfo;
using ::aidl::android::media::tv::tuner::TunerFilterSharedHandleInfo;
+using ::aidl::android::media::tv::tuner::TunerFilterTlvConfiguration;
using ::aidl::android::media::tv::tuner::TunerFilterTsConfiguration;
-using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
-using ::android::hardware::tv::tuner::V1_0::DemuxTpid;
+using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits;
using ::android::hardware::tv::tuner::V1_0::DemuxStreamId;
+using ::android::hardware::tv::tuner::V1_0::DemuxTpid;
using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings;
using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
using ::android::hardware::tv::tuner::V1_1::ScramblingStatus;
@@ -318,42 +325,317 @@
TunerFilterConfiguration FilterClient::getAidlFilterSettings(DemuxFilterSettings configure) {
TunerFilterConfiguration config;
- // TODO: complete filter setting conversion
switch (configure.getDiscriminator()) {
- case DemuxFilterSettings::hidl_discriminator::ts: {
- TunerFilterSettings filterSettings;
- switch (configure.ts().filterSettings.getDiscriminator()) {
- case DemuxTsFilterSettings::FilterSettings::hidl_discriminator::av: {
- TunerFilterAvSettings av{
- .isPassthrough = configure.ts().filterSettings.av().isPassthrough,
- };
- filterSettings.set<TunerFilterSettings::av>(av);
- break;
- }
- default:
- break;
- }
-
- TunerFilterTsConfiguration ts{
- .tpid = configure.ts().tpid,
- .filterSettings = filterSettings,
- };
- config.set<TunerFilterConfiguration::ts>(ts);
-
- return config;
- }
+ case DemuxFilterSettings::hidl_discriminator::ts:
+ return getAidlTsSettings(configure.ts());
case DemuxFilterSettings::hidl_discriminator::mmtp:
- break;
+ return getAidlMmtpSettings(configure.mmtp());
case DemuxFilterSettings::hidl_discriminator::ip:
- break;
+ return getAidlIpSettings(configure.ip());
case DemuxFilterSettings::hidl_discriminator::tlv:
- break;
+ return getAidlTlvSettings(configure.tlv());
+ case DemuxFilterSettings::hidl_discriminator::alp:
+ return getAidlAlpSettings(configure.alp());
default:
break;
}
+ ALOGE("Wrong DemuxFilterSettings union.");
return config;
}
+TunerFilterConfiguration FilterClient::getAidlTsSettings(DemuxTsFilterSettings ts) {
+ TunerFilterConfiguration config;
+ TunerFilterSettings filterSettings;
+ switch (ts.filterSettings.getDiscriminator()) {
+ case DemuxTsFilterSettings::FilterSettings::hidl_discriminator::av: {
+ filterSettings.set<TunerFilterSettings::av>(
+ getAidlAvSettings(ts.filterSettings.av()));
+ break;
+ }
+ case DemuxTsFilterSettings::FilterSettings::hidl_discriminator::section: {
+ filterSettings.set<TunerFilterSettings::section>(
+ getAidlSectionSettings(ts.filterSettings.section()));
+ break;
+ }
+ case DemuxTsFilterSettings::FilterSettings::hidl_discriminator::pesData: {
+ filterSettings.set<TunerFilterSettings::pesData>(
+ getAidlPesDataSettings(ts.filterSettings.pesData()));
+ break;
+ }
+ case DemuxTsFilterSettings::FilterSettings::hidl_discriminator::record: {
+ filterSettings.set<TunerFilterSettings::record>(
+ getAidlRecordSettings(ts.filterSettings.record()));
+ break;
+ }
+ default:
+ filterSettings.set<TunerFilterSettings::nothing>(true);
+ break;
+ }
+
+ TunerFilterTsConfiguration aidlTs{
+ .tpid = static_cast<char16_t>(ts.tpid),
+ .filterSettings = filterSettings,
+ };
+ config.set<TunerFilterConfiguration::ts>(aidlTs);
+
+ return config;
+}
+
+TunerFilterConfiguration FilterClient::getAidlMmtpSettings(DemuxMmtpFilterSettings mmtp) {
+ TunerFilterConfiguration config;
+ TunerFilterSettings filterSettings;
+ switch (mmtp.filterSettings.getDiscriminator()) {
+ case DemuxMmtpFilterSettings::FilterSettings::hidl_discriminator::av: {
+ filterSettings.set<TunerFilterSettings::av>(
+ getAidlAvSettings(mmtp.filterSettings.av()));
+ break;
+ }
+ case DemuxMmtpFilterSettings::FilterSettings::hidl_discriminator::section: {
+ filterSettings.set<TunerFilterSettings::section>(
+ getAidlSectionSettings(mmtp.filterSettings.section()));
+ break;
+ }
+ case DemuxMmtpFilterSettings::FilterSettings::hidl_discriminator::pesData: {
+ filterSettings.set<TunerFilterSettings::pesData>(
+ getAidlPesDataSettings(mmtp.filterSettings.pesData()));
+ break;
+ }
+ case DemuxMmtpFilterSettings::FilterSettings::hidl_discriminator::record: {
+ filterSettings.set<TunerFilterSettings::record>(
+ getAidlRecordSettings(mmtp.filterSettings.record()));
+ break;
+ }
+ case DemuxMmtpFilterSettings::FilterSettings::hidl_discriminator::download: {
+ filterSettings.set<TunerFilterSettings::download>(
+ getAidlDownloadSettings(mmtp.filterSettings.download()));
+ break;
+ }
+ default:
+ filterSettings.set<TunerFilterSettings::nothing>(true);
+ break;
+ }
+
+ TunerFilterMmtpConfiguration aidlMmtp{
+ .mmtpPid = static_cast<char16_t>(mmtp.mmtpPid),
+ .filterSettings = filterSettings,
+ };
+ config.set<TunerFilterConfiguration::mmtp>(aidlMmtp);
+
+ return config;
+}
+
+TunerFilterConfiguration FilterClient::getAidlIpSettings(DemuxIpFilterSettings ip) {
+ TunerFilterConfiguration config;
+ TunerFilterSettings filterSettings;
+ switch (ip.filterSettings.getDiscriminator()) {
+ case DemuxIpFilterSettings::FilterSettings::hidl_discriminator::section: {
+ filterSettings.set<TunerFilterSettings::section>(
+ getAidlSectionSettings(ip.filterSettings.section()));
+ break;
+ }
+ case DemuxIpFilterSettings::FilterSettings::hidl_discriminator::bPassthrough: {
+ filterSettings.set<TunerFilterSettings::isPassthrough>(
+ ip.filterSettings.bPassthrough());
+ break;
+ }
+ default:
+ filterSettings.set<TunerFilterSettings::nothing>(true);
+ break;
+ }
+
+ TunerDemuxIpAddressSettings ipAddr{
+ .srcPort = static_cast<char16_t>(ip.ipAddr.srcPort),
+ .dstPort = static_cast<char16_t>(ip.ipAddr.dstPort),
+ };
+ getAidlIpAddress(ip.ipAddr, ipAddr.srcIpAddress, ipAddr.dstIpAddress);
+
+ TunerFilterIpConfiguration aidlIp{
+ .ipAddr = ipAddr,
+ .filterSettings = filterSettings,
+ };
+ config.set<TunerFilterConfiguration::ip>(aidlIp);
+
+ return config;
+}
+
+void FilterClient::getAidlIpAddress(DemuxIpAddress ipAddr,
+ TunerDemuxIpAddress& srcIpAddress, TunerDemuxIpAddress& dstIpAddress) {
+ switch (ipAddr.srcIpAddress.getDiscriminator()) {
+ case DemuxIpAddress::SrcIpAddress::hidl_discriminator::v4: {
+ int size = ipAddr.srcIpAddress.v4().size();
+ srcIpAddress.isIpV6 = false;
+ srcIpAddress.addr.resize(ipAddr.srcIpAddress.v4().size());
+ copy(&ipAddr.srcIpAddress.v4()[0], &ipAddr.srcIpAddress.v4()[size],
+ srcIpAddress.addr.begin());
+ break;
+ }
+ case DemuxIpAddress::SrcIpAddress::hidl_discriminator::v6: {
+ int size = ipAddr.srcIpAddress.v6().size();
+ srcIpAddress.isIpV6 = true;
+ srcIpAddress.addr.resize(size);
+ copy(&ipAddr.srcIpAddress.v6()[0], &ipAddr.srcIpAddress.v6()[size],
+ srcIpAddress.addr.begin());
+ break;
+ }
+ default:
+ break;
+ }
+ switch (ipAddr.dstIpAddress.getDiscriminator()) {
+ case DemuxIpAddress::DstIpAddress::hidl_discriminator::v4: {
+ int size = ipAddr.dstIpAddress.v4().size();
+ dstIpAddress.isIpV6 = false;
+ dstIpAddress.addr.resize(size);
+ copy(&ipAddr.dstIpAddress.v4()[0], &ipAddr.dstIpAddress.v4()[size],
+ dstIpAddress.addr.begin());
+ break;
+ }
+ case DemuxIpAddress::DstIpAddress::hidl_discriminator::v6: {
+ int size = ipAddr.dstIpAddress.v6().size();
+ dstIpAddress.isIpV6 = true;
+ dstIpAddress.addr.resize(size);
+ copy(&ipAddr.dstIpAddress.v6()[0], &ipAddr.dstIpAddress.v6()[size],
+ dstIpAddress.addr.begin());
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+TunerFilterConfiguration FilterClient::getAidlTlvSettings(DemuxTlvFilterSettings tlv) {
+ TunerFilterConfiguration config;
+ TunerFilterSettings filterSettings;
+ switch (tlv.filterSettings.getDiscriminator()) {
+ case DemuxTlvFilterSettings::FilterSettings::hidl_discriminator::section: {
+ filterSettings.set<TunerFilterSettings::section>(
+ getAidlSectionSettings(tlv.filterSettings.section()));
+ break;
+ }
+ case DemuxTlvFilterSettings::FilterSettings::hidl_discriminator::bPassthrough: {
+ filterSettings.set<TunerFilterSettings::isPassthrough>(
+ tlv.filterSettings.bPassthrough());
+ break;
+ }
+ default:
+ filterSettings.set<TunerFilterSettings::nothing>(true);
+ break;
+ }
+
+ TunerFilterTlvConfiguration aidlTlv{
+ .packetType = static_cast<int8_t>(tlv.packetType),
+ .isCompressedIpPacket = tlv.isCompressedIpPacket,
+ .filterSettings = filterSettings,
+ };
+ config.set<TunerFilterConfiguration::tlv>(aidlTlv);
+
+ return config;
+}
+
+TunerFilterConfiguration FilterClient::getAidlAlpSettings(DemuxAlpFilterSettings alp) {
+ TunerFilterConfiguration config;
+ TunerFilterSettings filterSettings;
+ switch (alp.filterSettings.getDiscriminator()) {
+ case DemuxAlpFilterSettings::FilterSettings::hidl_discriminator::section: {
+ filterSettings.set<TunerFilterSettings::section>(
+ getAidlSectionSettings(alp.filterSettings.section()));
+ break;
+ }
+ default:
+ filterSettings.set<TunerFilterSettings::nothing>(true);
+ break;
+ }
+
+ TunerFilterAlpConfiguration aidlAlp{
+ .packetType = static_cast<int8_t>(alp.packetType),
+ .lengthType = static_cast<int8_t>(alp.lengthType),
+ .filterSettings = filterSettings,
+ };
+ config.set<TunerFilterConfiguration::alp>(aidlAlp);
+
+ return config;
+}
+
+TunerFilterAvSettings FilterClient::getAidlAvSettings(DemuxFilterAvSettings hidlAv) {
+ TunerFilterAvSettings aidlAv{
+ .isPassthrough = hidlAv.isPassthrough,
+ };
+ return aidlAv;
+}
+
+TunerFilterSectionSettings FilterClient::getAidlSectionSettings(
+ DemuxFilterSectionSettings hidlSection) {
+ TunerFilterSectionSettings aidlSection;
+
+ switch (hidlSection.condition.getDiscriminator()) {
+ case DemuxFilterSectionSettings::Condition::hidl_discriminator::sectionBits: {
+ TunerFilterSectionBits sectionBits;
+ auto hidlSectionBits = hidlSection.condition.sectionBits();
+ sectionBits.filter.resize(hidlSectionBits.filter.size());
+ sectionBits.mask.resize(hidlSectionBits.mask.size());
+ sectionBits.mode.resize(hidlSectionBits.mode.size());
+ copy(hidlSectionBits.filter.begin(), hidlSectionBits.filter.end(),
+ hidlSectionBits.filter.begin());
+ copy(hidlSectionBits.mask.begin(), hidlSectionBits.mask.end(),
+ hidlSectionBits.mask.begin());
+ copy(hidlSectionBits.mode.begin(), hidlSectionBits.mode.end(),
+ hidlSectionBits.mode.begin());
+ aidlSection.condition.set<TunerFilterSectionCondition::sectionBits>(sectionBits);
+ break;
+ }
+ case DemuxFilterSectionSettings::Condition::hidl_discriminator::tableInfo: {
+ TunerFilterSectionTableInfo tableInfo{
+ .tableId = static_cast<char16_t>(hidlSection.condition.tableInfo().tableId),
+ .version = static_cast<char16_t>(hidlSection.condition.tableInfo().version),
+ };
+ aidlSection.condition.set<TunerFilterSectionCondition::tableInfo>(tableInfo);
+ break;
+ }
+ }
+ aidlSection.isCheckCrc = hidlSection.isCheckCrc;
+ aidlSection.isRepeat = hidlSection.isRepeat;
+ aidlSection.isRaw = hidlSection.isRaw;
+ return aidlSection;
+}
+
+TunerFilterPesDataSettings FilterClient::getAidlPesDataSettings(
+ DemuxFilterPesDataSettings hidlPesData) {
+ TunerFilterPesDataSettings aidlPesData{
+ .streamId = static_cast<char16_t>(hidlPesData.streamId),
+ .isRaw = hidlPesData.isRaw,
+ };
+ return aidlPesData;
+}
+
+TunerFilterRecordSettings FilterClient::getAidlRecordSettings(
+ DemuxFilterRecordSettings hidlRecord) {
+ TunerFilterScIndexMask mask;
+ switch (hidlRecord.scIndexMask.getDiscriminator()) {
+ case DemuxFilterRecordSettings::ScIndexMask::hidl_discriminator::sc: {
+ mask.set<TunerFilterScIndexMask::sc>(hidlRecord.scIndexMask.sc());
+ break;
+ }
+ case DemuxFilterRecordSettings::ScIndexMask::hidl_discriminator::scHevc: {
+ mask.set<TunerFilterScIndexMask::scHevc>(hidlRecord.scIndexMask.scHevc());
+ break;
+ }
+ default:
+ break;
+ }
+ TunerFilterRecordSettings aidlRecord{
+ .tsIndexMask = static_cast<int32_t>(hidlRecord.tsIndexMask),
+ .scIndexType = static_cast<int32_t>(hidlRecord.scIndexType),
+ .scIndexMask = mask,
+ };
+ return aidlRecord;
+}
+
+TunerFilterDownloadSettings FilterClient::getAidlDownloadSettings(
+ DemuxFilterDownloadSettings hidlDownload) {
+ TunerFilterDownloadSettings aidlDownload{
+ .downloadId = static_cast<int32_t>(hidlDownload.downloadId),
+ };
+ return aidlDownload;
+}
void TunerFilterCallback::getHidlFilterEvent(const vector<TunerFilterEvent>& filterEvents,
DemuxFilterEvent& event, DemuxFilterEventExt& eventExt) {
diff --git a/media/jni/tuner/FilterClient.h b/media/jni/tuner/FilterClient.h
index 89eee3c..21919ac 100644
--- a/media/jni/tuner/FilterClient.h
+++ b/media/jni/tuner/FilterClient.h
@@ -33,8 +33,14 @@
using Status = ::ndk::ScopedAStatus;
using ::aidl::android::media::tv::tuner::BnTunerFilterCallback;
using ::aidl::android::media::tv::tuner::ITunerFilter;
+using ::aidl::android::media::tv::tuner::TunerDemuxIpAddress;
+using ::aidl::android::media::tv::tuner::TunerFilterAvSettings;
using ::aidl::android::media::tv::tuner::TunerFilterConfiguration;
+using ::aidl::android::media::tv::tuner::TunerFilterDownloadSettings;
using ::aidl::android::media::tv::tuner::TunerFilterEvent;
+using ::aidl::android::media::tv::tuner::TunerFilterPesDataSettings;
+using ::aidl::android::media::tv::tuner::TunerFilterRecordSettings;
+using ::aidl::android::media::tv::tuner::TunerFilterSectionSettings;
using ::aidl::android::media::tv::tuner::TunerFilterSettings;
using ::android::hardware::EventFlag;
@@ -43,8 +49,19 @@
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::hardware::hidl_handle;
+using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterAvSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterRecordSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
+using ::android::hardware::tv::tuner::V1_0::DemuxIpAddress;
+using ::android::hardware::tv::tuner::V1_0::DemuxIpFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxTlvFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings;
using ::android::hardware::tv::tuner::V1_0::IFilter;
using ::android::hardware::tv::tuner::V1_0::Result;
using ::android::hardware::tv::tuner::V1_1::AvStreamType;
@@ -201,6 +218,21 @@
private:
TunerFilterConfiguration getAidlFilterSettings(DemuxFilterSettings configure);
+
+ TunerFilterConfiguration getAidlTsSettings(DemuxTsFilterSettings configure);
+ TunerFilterConfiguration getAidlMmtpSettings(DemuxMmtpFilterSettings mmtp);
+ TunerFilterConfiguration getAidlIpSettings(DemuxIpFilterSettings ip);
+ TunerFilterConfiguration getAidlTlvSettings(DemuxTlvFilterSettings tlv);
+ TunerFilterConfiguration getAidlAlpSettings(DemuxAlpFilterSettings alp);
+
+ TunerFilterAvSettings getAidlAvSettings(DemuxFilterAvSettings hidlAv);
+ TunerFilterSectionSettings getAidlSectionSettings(DemuxFilterSectionSettings hidlSection);
+ TunerFilterPesDataSettings getAidlPesDataSettings(DemuxFilterPesDataSettings hidlPesData);
+ TunerFilterRecordSettings getAidlRecordSettings(DemuxFilterRecordSettings hidlRecord);
+ TunerFilterDownloadSettings getAidlDownloadSettings(DemuxFilterDownloadSettings hidlDownload);
+
+ void getAidlIpAddress(DemuxIpAddress ipAddr,
+ TunerDemuxIpAddress& srcIpAddress, TunerDemuxIpAddress& dstIpAddress);
Result getFilterMq();
int copyData(uint8_t* buffer, int size);
void checkIsMediaFilter(DemuxFilterType type);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index ca9dcd6..5af0244 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -116,7 +116,6 @@
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.MONITOR_INPUT" />
<uses-permission android:name="android.permission.INPUT_CONSUMER" />
- <uses-permission android:name="android.permission.USE_BACKGROUND_BLUR" />
<!-- DreamManager -->
<uses-permission android:name="android.permission.READ_DREAM_STATE" />
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index fc68a64..2a055fc 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -39,6 +39,25 @@
tools:minHeight="100dp"
tools:minWidth="100dp" />
+ <com.android.systemui.screenshot.CropView
+ android:id="@+id/crop_view"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginVertical="8dp"
+ app:layout_constrainedHeight="true"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintBottom_toBottomOf="@id/guideline"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:handleThickness="3dp"
+ app:handleColor="@*android:color/accent_device_default"
+ app:scrimColor="#9444"
+ tools:background="?android:colorBackground"
+ tools:minHeight="100dp"
+ tools:minWidth="100dp" />
+
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/udfps_animation_view.xml b/packages/SystemUI/res/layout/udfps_animation_view.xml
new file mode 100644
index 0000000..380dd85
--- /dev/null
+++ b/packages/SystemUI/res/layout/udfps_animation_view.xml
@@ -0,0 +1,21 @@
+<?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.systemui.biometrics.UdfpsAnimationView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/udfps_animation_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
diff --git a/packages/SystemUI/res/layout/udfps_surface_view.xml b/packages/SystemUI/res/layout/udfps_surface_view.xml
new file mode 100644
index 0000000..18858d6
--- /dev/null
+++ b/packages/SystemUI/res/layout/udfps_surface_view.xml
@@ -0,0 +1,21 @@
+<?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.systemui.biometrics.UdfpsSurfaceView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/udfps_surface_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 4059b49..e2fe223 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -171,5 +171,11 @@
<declare-styleable name="PagedTileLayout">
<attr name="sideLabels" format="boolean"/>
</declare-styleable>
+
+ <declare-styleable name="CropView">
+ <attr name="handleThickness" format="dimension" />
+ <attr name="handleColor" format="color" />
+ <attr name="scrimColor" format="color" />
+ </declare-styleable>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index eee0128..81b7a7b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2580,9 +2580,6 @@
<!-- What to show on the ambient display player when song doesn't have a title. [CHAR LIMIT=20] -->
<string name="music_controls_no_title">No title</string>
- <!-- Description of the restart button in the hint of size compatibility mode. [CHAR LIMIT=NONE] -->
- <string name="restart_button_description">Tap to restart this app and go full screen.</string>
-
<!-- Action in accessibility menu to move the stack of bubbles [CHAR LIMIT=20] -->
<string name="bubble_accessibility_action_move">Move</string>
diff --git a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
index 5384ddf..7a52d27 100644
--- a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
@@ -16,284 +16,27 @@
package com.android.systemui;
-import android.app.ActivityClient;
import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.RippleDrawable;
-import android.hardware.display.DisplayManager;
-import android.inputmethodservice.InputMethodService;
-import android.os.IBinder;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.Display;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.PopupWindow;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.shared.system.TaskStackChangeListeners;
-import com.android.systemui.statusbar.CommandQueue;
-
-import java.lang.ref.WeakReference;
import javax.inject.Inject;
-/** Shows a restart-activity button when the foreground activity is in size compatibility mode. */
+/**
+ * Shows a restart-activity button when the foreground activity is in size compatibility mode.
+ *
+ * // TODO remove this class after cleanup all dependencies.
+ * @deprecated Use {@link com.android.wm.shell.sizecompatui.SizeCompatUIController}
+ */
+@Deprecated
@SysUISingleton
-public class SizeCompatModeActivityController extends SystemUI implements CommandQueue.Callbacks {
- private static final String TAG = "SizeCompatMode";
+public class SizeCompatModeActivityController extends SystemUI {
- /** The showing buttons by display id. */
- private final SparseArray<RestartActivityButton> mActiveButtons = new SparseArray<>(1);
- /** Avoid creating display context frequently for non-default display. */
- private final SparseArray<WeakReference<Context>> mDisplayContextCache = new SparseArray<>(0);
- private final CommandQueue mCommandQueue;
-
- /** Only show once automatically in the process life. */
- private boolean mHasShownHint;
-
- @VisibleForTesting
@Inject
- SizeCompatModeActivityController(Context context, TaskStackChangeListeners listeners,
- CommandQueue commandQueue) {
+ SizeCompatModeActivityController(Context context) {
super(context);
- mCommandQueue = commandQueue;
- listeners.registerTaskStackListener(new TaskStackChangeListener() {
- @Override
- public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) {
- // Note the callback already runs on main thread.
- updateRestartButton(displayId, activityToken);
- }
- });
}
@Override
- public void start() {
- mCommandQueue.addCallback(this);
- }
-
- @Override
- public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
- boolean showImeSwitcher) {
- RestartActivityButton button = mActiveButtons.get(displayId);
- if (button == null) {
- return;
- }
- boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
- int newVisibility = imeShown ? View.GONE : View.VISIBLE;
- // Hide the button when input method is showing.
- if (button.getVisibility() != newVisibility) {
- button.setVisibility(newVisibility);
- }
- }
-
- @Override
- public void onDisplayRemoved(int displayId) {
- mDisplayContextCache.remove(displayId);
- removeRestartButton(displayId);
- }
-
- private void removeRestartButton(int displayId) {
- RestartActivityButton button = mActiveButtons.get(displayId);
- if (button != null) {
- button.remove();
- mActiveButtons.remove(displayId);
- }
- }
-
- private void updateRestartButton(int displayId, IBinder activityToken) {
- if (activityToken == null) {
- // Null token means the current foreground activity is not in size compatibility mode.
- removeRestartButton(displayId);
- return;
- }
-
- RestartActivityButton restartButton = mActiveButtons.get(displayId);
- if (restartButton != null) {
- restartButton.updateLastTargetActivity(activityToken);
- return;
- }
-
- Context context = getOrCreateDisplayContext(displayId);
- if (context == null) {
- Log.i(TAG, "Cannot get context for display " + displayId);
- return;
- }
-
- restartButton = createRestartButton(context);
- restartButton.updateLastTargetActivity(activityToken);
- if (restartButton.show()) {
- mActiveButtons.append(displayId, restartButton);
- } else {
- onDisplayRemoved(displayId);
- }
- }
-
- @VisibleForTesting
- RestartActivityButton createRestartButton(Context context) {
- RestartActivityButton button = new RestartActivityButton(context, mHasShownHint);
- mHasShownHint = true;
- return button;
- }
-
- private Context getOrCreateDisplayContext(int displayId) {
- if (displayId == Display.DEFAULT_DISPLAY) {
- return mContext;
- }
- Context context = null;
- WeakReference<Context> ref = mDisplayContextCache.get(displayId);
- if (ref != null) {
- context = ref.get();
- }
- if (context == null) {
- Display display = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
- if (display != null) {
- context = mContext.createDisplayContext(display);
- mDisplayContextCache.put(displayId, new WeakReference<Context>(context));
- }
- }
- return context;
- }
-
- @VisibleForTesting
- static class RestartActivityButton extends ImageButton implements View.OnClickListener,
- View.OnLongClickListener {
-
- final WindowManager.LayoutParams mWinParams;
- final boolean mShouldShowHint;
- IBinder mLastActivityToken;
-
- final int mPopupOffsetX;
- final int mPopupOffsetY;
- PopupWindow mShowingHint;
-
- RestartActivityButton(Context context, boolean hasShownHint) {
- super(context);
- mShouldShowHint = !hasShownHint;
- Drawable drawable = context.getDrawable(R.drawable.btn_restart);
- setImageDrawable(drawable);
- setContentDescription(context.getString(R.string.restart_button_description));
-
- int drawableW = drawable.getIntrinsicWidth();
- int drawableH = drawable.getIntrinsicHeight();
- mPopupOffsetX = drawableW / 2;
- mPopupOffsetY = drawableH * 2;
-
- ColorStateList color = ColorStateList.valueOf(Color.LTGRAY);
- GradientDrawable mask = new GradientDrawable();
- mask.setShape(GradientDrawable.OVAL);
- mask.setColor(color);
- setBackground(new RippleDrawable(color, null /* content */, mask));
- setOnClickListener(this);
- setOnLongClickListener(this);
-
- mWinParams = new WindowManager.LayoutParams();
- mWinParams.gravity = getGravity(getResources().getConfiguration().getLayoutDirection());
- mWinParams.width = drawableW * 2;
- mWinParams.height = drawableH * 2;
- mWinParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
- mWinParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
- mWinParams.format = PixelFormat.TRANSLUCENT;
- mWinParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
- mWinParams.setTitle(SizeCompatModeActivityController.class.getSimpleName()
- + context.getDisplayId());
- }
-
- void updateLastTargetActivity(IBinder activityToken) {
- mLastActivityToken = activityToken;
- }
-
- /** @return {@code false} if the target display is invalid. */
- boolean show() {
- try {
- getContext().getSystemService(WindowManager.class).addView(this, mWinParams);
- } catch (WindowManager.InvalidDisplayException e) {
- // The target display may have been removed when the callback has just arrived.
- Log.w(TAG, "Cannot show on display " + getContext().getDisplayId(), e);
- return false;
- }
- return true;
- }
-
- void remove() {
- if (mShowingHint != null) {
- mShowingHint.dismiss();
- }
- getContext().getSystemService(WindowManager.class).removeViewImmediate(this);
- }
-
- @Override
- public void onClick(View v) {
- ActivityClient.getInstance().restartActivityProcessIfVisible(mLastActivityToken);
- }
-
- @Override
- public boolean onLongClick(View v) {
- showHint();
- return true;
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (mShouldShowHint) {
- showHint();
- }
- }
-
- @Override
- public void setLayoutDirection(int layoutDirection) {
- int gravity = getGravity(layoutDirection);
- if (mWinParams.gravity != gravity) {
- mWinParams.gravity = gravity;
- if (mShowingHint != null) {
- mShowingHint.dismiss();
- showHint();
- }
- getContext().getSystemService(WindowManager.class).updateViewLayout(this,
- mWinParams);
- }
- super.setLayoutDirection(layoutDirection);
- }
-
- void showHint() {
- if (mShowingHint != null) {
- return;
- }
-
- View popupView = LayoutInflater.from(getContext()).inflate(
- R.layout.size_compat_mode_hint, null /* root */);
- PopupWindow popupWindow = new PopupWindow(popupView,
- LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
- popupWindow.setWindowLayoutType(mWinParams.type);
- popupWindow.setElevation(getResources().getDimension(R.dimen.bubble_elevation));
- popupWindow.setAnimationStyle(android.R.style.Animation_InputMethod);
- popupWindow.setClippingEnabled(false);
- popupWindow.setOnDismissListener(() -> mShowingHint = null);
- mShowingHint = popupWindow;
-
- Button gotItButton = popupView.findViewById(R.id.got_it);
- gotItButton.setBackground(new RippleDrawable(ColorStateList.valueOf(Color.LTGRAY),
- null /* content */, null /* mask */));
- gotItButton.setOnClickListener(view -> popupWindow.dismiss());
- popupWindow.showAtLocation(this, mWinParams.gravity, mPopupOffsetX, mPopupOffsetY);
- }
-
- private static int getGravity(int layoutDirection) {
- return Gravity.BOTTOM
- | (layoutDirection == View.LAYOUT_DIRECTION_RTL ? Gravity.START : Gravity.END);
- }
- }
+ public void start() { }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/HbmCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/HbmCallback.java
new file mode 100644
index 0000000..2083f2e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/HbmCallback.java
@@ -0,0 +1,41 @@
+/*
+ * 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.biometrics;
+
+import android.annotation.NonNull;
+import android.view.Surface;
+
+/**
+ * Interface for controlling the high-brightness mode (HBM). UdfpsView can use this callback to
+ * enable the HBM while showing the fingerprint illumination, and to disable the HBM after the
+ * illumination is no longer necessary.
+ */
+public interface HbmCallback {
+ /**
+ * UdfpsView will call this to enable the HBM before drawing the illumination dot.
+ *
+ * @param surface A valid surface for which the HBM should be enabled.
+ */
+ void enableHbm(@NonNull Surface surface);
+
+ /**
+ * UdfpsView will call this to disable the HBM when the illumination is not longer needed.
+ *
+ * @param surface A valid surface for which the HBM should be disabled.
+ */
+ void disableHbm(@NonNull Surface surface);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
index 40fe7b1..3ea8140 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
@@ -52,4 +52,18 @@
public void setAlpha(int alpha) {
mFingerprintDrawable.setAlpha(alpha);
}
+
+ /**
+ * @return The amount of padding that's needed on each side of the sensor, in pixels.
+ */
+ public int getPaddingX() {
+ return 0;
+ }
+
+ /**
+ * @return The amount of padding that's needed on each side of the sensor, in pixels.
+ */
+ public int getPaddingY() {
+ return 0;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
index 52662ae..68f1414 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
@@ -70,6 +70,9 @@
@Override
public void draw(@NonNull Canvas canvas) {
+ canvas.save();
+ canvas.translate(getPaddingX(), getPaddingY());
+
final boolean isNightMode = (mContext.getResources().getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_YES) != 0;
if (!isNightMode) {
@@ -78,6 +81,18 @@
}
}
mFingerprintDrawable.draw(canvas);
+
+ canvas.restore();
+ }
+
+ @Override
+ public int getPaddingX() {
+ return (int) Math.ceil(SHADOW_RADIUS);
+ }
+
+ @Override
+ public int getPaddingY() {
+ return (int) Math.ceil(SHADOW_RADIUS);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
new file mode 100644
index 0000000..fded737
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -0,0 +1,106 @@
+/*
+ * 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.biometrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.systemui.doze.DozeReceiver;
+import com.android.systemui.statusbar.phone.ScrimController;
+
+/**
+ * Class that coordinates non-HBM animations (such as enroll, keyguard, BiometricPrompt,
+ * FingerprintManager).
+ */
+public class UdfpsAnimationView extends View implements DozeReceiver,
+ ScrimController.ScrimChangedListener {
+
+ private static final String TAG = "UdfpsAnimationView";
+
+ @NonNull private UdfpsView mParent;
+ @Nullable private UdfpsAnimation mUdfpsAnimation;
+ @NonNull private RectF mSensorRect;
+ private int mNotificationPanelAlpha;
+
+
+ public UdfpsAnimationView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ mSensorRect = new RectF();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ if (mUdfpsAnimation != null) {
+ final int alpha = mParent.shouldPauseAuth() ? 255 - mNotificationPanelAlpha : 255;
+ mUdfpsAnimation.setAlpha(alpha);
+ mUdfpsAnimation.draw(canvas);
+ }
+ }
+
+ void setParent(@NonNull UdfpsView parent) {
+ mParent = parent;
+ }
+
+ void setAnimation(@Nullable UdfpsAnimation animation) {
+ mUdfpsAnimation = animation;
+ }
+
+ void onSensorRectUpdated(@NonNull RectF sensorRect) {
+ mSensorRect = sensorRect;
+ if (mUdfpsAnimation != null) {
+ mUdfpsAnimation.onSensorRectUpdated(mSensorRect);
+ }
+ }
+
+ void updateColor() {
+ if (mUdfpsAnimation != null) {
+ mUdfpsAnimation.updateColor();
+ }
+ }
+
+ @Override
+ public void dozeTimeTick() {
+ if (mUdfpsAnimation instanceof DozeReceiver) {
+ ((DozeReceiver) mUdfpsAnimation).dozeTimeTick();
+ }
+ }
+
+ @Override
+ public void onAlphaChanged(float alpha) {
+ mNotificationPanelAlpha = (int) (alpha * 255);
+ postInvalidate();
+ }
+
+ void onEnrollmentProgress(int remaining) {
+ if (mUdfpsAnimation instanceof UdfpsAnimationEnroll) {
+ ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentProgress(remaining);
+ }
+ }
+
+ void onEnrollmentHelp() {
+ if (mUdfpsAnimation instanceof UdfpsAnimationEnroll) {
+ ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentHelp();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 008e8f5..ac4b93a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -62,7 +62,7 @@
*/
@SuppressWarnings("deprecation")
@SysUISingleton
-public class UdfpsController implements UdfpsView.HbmCallback, DozeReceiver {
+public class UdfpsController implements DozeReceiver, HbmCallback {
private static final String TAG = "UdfpsController";
private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000;
@@ -242,12 +242,15 @@
}
}
- private WindowManager.LayoutParams computeLayoutParams() {
+ private WindowManager.LayoutParams computeLayoutParams(@Nullable UdfpsAnimation animation) {
+ final int paddingX = animation != null ? animation.getPaddingX() : 0;
+ final int paddingY = animation != null ? animation.getPaddingY() : 0;
+
// Default dimensions assume portrait mode.
- mCoreLayoutParams.x = mSensorProps.sensorLocationX - mSensorProps.sensorRadius;
- mCoreLayoutParams.y = mSensorProps.sensorLocationY - mSensorProps.sensorRadius;
- mCoreLayoutParams.height = 2 * mSensorProps.sensorRadius;
- mCoreLayoutParams.width = 2 * mSensorProps.sensorRadius;
+ mCoreLayoutParams.x = mSensorProps.sensorLocationX - mSensorProps.sensorRadius - paddingX;
+ mCoreLayoutParams.y = mSensorProps.sensorLocationY - mSensorProps.sensorRadius - paddingY;
+ mCoreLayoutParams.height = 2 * mSensorProps.sensorRadius + 2 * paddingX;
+ mCoreLayoutParams.width = 2 * mSensorProps.sensorRadius + 2 * paddingY;
Point p = new Point();
// Gets the size based on the current rotation of the display.
@@ -289,8 +292,9 @@
if (!mIsOverlayShowing) {
try {
Log.v(TAG, "showUdfpsOverlay | adding window");
- mView.setUdfpsAnimation(getUdfpsAnimationForReason(reason));
- mWindowManager.addView(mView, computeLayoutParams());
+ final UdfpsAnimation animation = getUdfpsAnimationForReason(reason);
+ mView.setUdfpsAnimation(animation);
+ mWindowManager.addView(mView, computeLayoutParams(animation));
mView.setOnTouchListener(mOnTouchListener);
mIsOverlayShowing = true;
} catch (RuntimeException e) {
@@ -374,9 +378,8 @@
// This method can be called from the UI thread.
private void onFingerDown(int x, int y, float minor, float major) {
- mView.setOnIlluminatedRunnable(
- () -> mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major));
- mView.startIllumination();
+ mView.startIllumination(() ->
+ mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major));
}
// This method can be called from the UI thread.
@@ -386,13 +389,13 @@
}
@Override
- public void enableHbm(Surface surface) {
+ public void enableHbm(@NonNull Surface surface) {
// Do nothing. This method can be implemented for devices that require the high-brightness
// mode for fingerprint illumination.
}
@Override
- public void disableHbm(Surface surface) {
+ public void disableHbm(@NonNull Surface surface) {
// Do nothing. This method can be implemented for devices that require the high-brightness
// mode for fingerprint illumination.
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java
new file mode 100644
index 0000000..8bea05b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java
@@ -0,0 +1,41 @@
+/*
+ * 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.biometrics;
+
+import android.annotation.Nullable;
+
+/**
+ * Interface that should be implemented by UI's that need to coordinate user touches,
+ * views/animations, and modules that start/stop display illumination.
+ */
+interface UdfpsIlluminator {
+ /**
+ * @param callback Invoked when HBM should be enabled or disabled.
+ */
+ void setHbmCallback(@Nullable HbmCallback callback);
+
+ /**
+ * Invoked when illumination should start.
+ * @param onIlluminatedRunnable Invoked when the display has been illuminated.
+ */
+ void startIllumination(@Nullable Runnable onIlluminatedRunnable);
+
+ /**
+ * Invoked when illumination should end.
+ */
+ void stopIllumination();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
new file mode 100644
index 0000000..97c215e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
@@ -0,0 +1,115 @@
+/*
+ * 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.biometrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+/**
+ * Under-display fingerprint sensor Surface View. The surface should be used for HBM-specific things
+ * only. All other animations should be done on the other view.
+ */
+public class UdfpsSurfaceView extends SurfaceView implements UdfpsIlluminator {
+ private static final String TAG = "UdfpsSurfaceView";
+
+ /**
+ * This is used instead of {@link android.graphics.drawable.Drawable}, because the latter has
+ * several abstract methods that are not used here but require implementation.
+ */
+ private interface SimpleDrawable {
+ void draw(Canvas canvas);
+ }
+
+ @NonNull private final SurfaceHolder mHolder;
+ @NonNull private final Paint mSensorPaint;
+ @NonNull private final SimpleDrawable mIlluminationDotDrawable;
+
+ @NonNull private RectF mSensorRect;
+ @Nullable private HbmCallback mHbmCallback;
+
+ public UdfpsSurfaceView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ mHolder = getHolder();
+ mHolder.setFormat(PixelFormat.RGBA_8888);
+
+ mSensorRect = new RectF();
+ mSensorPaint = new Paint(0 /* flags */);
+ mSensorPaint.setAntiAlias(true);
+ mSensorPaint.setARGB(255, 255, 255, 255);
+ mSensorPaint.setStyle(Paint.Style.FILL);
+
+ mIlluminationDotDrawable = canvas -> canvas.drawOval(mSensorRect, mSensorPaint);
+ }
+
+ @Override
+ public void setHbmCallback(@Nullable HbmCallback callback) {
+ mHbmCallback = callback;
+ }
+
+ @Override
+ public void startIllumination(@Nullable Runnable onIlluminatedRunnable) {
+ if (mHbmCallback != null && mHolder.getSurface().isValid()) {
+ mHbmCallback.enableHbm(mHolder.getSurface());
+ }
+ drawImmediately(mIlluminationDotDrawable);
+
+ if (onIlluminatedRunnable != null) {
+ // No framework API can reliably tell when a frame reaches the panel. A timeout is the
+ // safest solution. The frame should be displayed within 3 refresh cycles, which on a
+ // 60 Hz panel equates to 50 milliseconds.
+ postDelayed(onIlluminatedRunnable, 50 /* delayMillis */);
+ }
+ }
+
+ @Override
+ public void stopIllumination() {
+ if (mHbmCallback != null && mHolder.getSurface().isValid()) {
+ mHbmCallback.disableHbm(mHolder.getSurface());
+ }
+
+ invalidate();
+ }
+
+ void onSensorRectUpdated(@NonNull RectF sensorRect) {
+ mSensorRect = sensorRect;
+ }
+
+ /**
+ * Immediately draws the provided drawable on this SurfaceView's surface.
+ */
+ private void drawImmediately(@NonNull SimpleDrawable drawable) {
+ Canvas canvas = null;
+ try {
+ canvas = mHolder.lockCanvas();
+ drawable.draw(canvas);
+ } finally {
+ // Make sure the surface is never left in a bad state.
+ if (canvas != null) {
+ mHolder.unlockCanvasAndPost(canvas);
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index 4c05b88..d448ed8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -27,16 +27,14 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
import android.graphics.RectF;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
import com.android.systemui.R;
import com.android.systemui.doze.DozeReceiver;
@@ -44,56 +42,19 @@
import com.android.systemui.statusbar.phone.ScrimController;
/**
- * A full screen view with a configurable illumination dot and scrim.
+ * A view containing 1) A SurfaceView for HBM, and 2) A normal drawable view for all other
+ * animations.
*/
-public class UdfpsView extends SurfaceView implements DozeReceiver,
+public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIlluminator,
StatusBarStateController.StateListener, ScrimController.ScrimChangedListener {
private static final String TAG = "UdfpsView";
- /**
- * Interface for controlling the high-brightness mode (HBM). UdfpsView can use this callback to
- * enable the HBM while showing the fingerprint illumination, and to disable the HBM after the
- * illumination is no longer necessary.
- */
- interface HbmCallback {
- /**
- * UdfpsView will call this to enable the HBM before drawing the illumination dot.
- *
- * @param surface A valid surface for which the HBM should be enabled.
- */
- void enableHbm(@NonNull Surface surface);
-
- /**
- * UdfpsView will call this to disable the HBM when the illumination is not longer needed.
- *
- * @param surface A valid surface for which the HBM should be disabled.
- */
- void disableHbm(@NonNull Surface surface);
- }
-
- /**
- * This is used instead of {@link android.graphics.drawable.Drawable}, because the latter has
- * several abstract methods that are not used here but require implementation.
- */
- private interface SimpleDrawable {
- void draw(Canvas canvas);
- }
-
- // Radius in pixels.
- private static final float SENSOR_SHADOW_RADIUS = 2.0f;
-
private static final int DEBUG_TEXT_SIZE_PX = 32;
- @NonNull private final SurfaceHolder mHolder;
+ @NonNull private final UdfpsSurfaceView mHbmSurfaceView;
+ @NonNull private final UdfpsAnimationView mAnimationView;
@NonNull private final RectF mSensorRect;
- @NonNull private final Paint mSensorPaint;
@NonNull private final Paint mDebugTextPaint;
- @NonNull private final SimpleDrawable mIlluminationDotDrawable;
- @NonNull private final SimpleDrawable mClearSurfaceDrawable;
-
- @Nullable private UdfpsAnimation mUdfpsAnimation;
- @Nullable private HbmCallback mHbmCallback;
- @Nullable private Runnable mOnIlluminatedRunnable;
// Used to obtain the sensor location.
@NonNull private FingerprintSensorPropertiesInternal mSensorProps;
@@ -103,7 +64,6 @@
private boolean mIlluminationRequested;
private int mStatusBarState;
private boolean mNotificationShadeExpanded;
- private int mNotificationPanelAlpha;
public UdfpsView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -121,27 +81,27 @@
a.recycle();
}
- mHolder = getHolder();
- mHolder.setFormat(PixelFormat.RGBA_8888);
+ // Inflate UdfpsSurfaceView
+ final LayoutInflater inflater = LayoutInflater.from(context);
+ mHbmSurfaceView = (UdfpsSurfaceView) inflater.inflate(R.layout.udfps_surface_view,
+ null, false);
+ addView(mHbmSurfaceView);
+ mHbmSurfaceView.setVisibility(View.INVISIBLE);
+
+ // Inflate UdfpsAnimationView
+ mAnimationView = (UdfpsAnimationView) inflater.inflate(R.layout.udfps_animation_view,
+ null, false);
+ mAnimationView.setParent(this);
+ addView(mAnimationView);
mSensorRect = new RectF();
- mSensorPaint = new Paint(0 /* flags */);
- mSensorPaint.setAntiAlias(true);
- mSensorPaint.setARGB(255, 255, 255, 255);
- mSensorPaint.setStyle(Paint.Style.FILL);
mDebugTextPaint = new Paint();
mDebugTextPaint.setAntiAlias(true);
mDebugTextPaint.setColor(Color.BLUE);
mDebugTextPaint.setTextSize(DEBUG_TEXT_SIZE_PX);
- mIlluminationDotDrawable = canvas -> canvas.drawOval(mSensorRect, mSensorPaint);
- mClearSurfaceDrawable = canvas -> canvas.drawColor(0, PorterDuff.Mode.CLEAR);
-
mIlluminationRequested = false;
- // SurfaceView sets this to true by default. We must set it to false to allow
- // onDraw to be called.
- setWillNotDraw(false);
}
void setSensorProperties(@NonNull FingerprintSensorPropertiesInternal properties) {
@@ -149,29 +109,17 @@
}
void setUdfpsAnimation(@Nullable UdfpsAnimation animation) {
- mUdfpsAnimation = animation;
+ mAnimationView.setAnimation(animation);
}
- /**
- * Sets a callback that can be used to enable and disable the high-brightness mode (HBM).
- */
- void setHbmCallback(@Nullable HbmCallback callback) {
- mHbmCallback = callback;
- }
-
- /**
- * Sets a runnable that will be run when the first illumination frame reaches the panel.
- * The runnable is reset to null after it is executed once.
- */
- void setOnIlluminatedRunnable(Runnable runnable) {
- mOnIlluminatedRunnable = runnable;
+ @Override
+ public void setHbmCallback(@Nullable HbmCallback callback) {
+ mHbmSurfaceView.setHbmCallback(callback);
}
@Override
public void dozeTimeTick() {
- if (mUdfpsAnimation instanceof DozeReceiver) {
- ((DozeReceiver) mUdfpsAnimation).dozeTimeTick();
- }
+ mAnimationView.dozeTimeTick();
}
@Override
@@ -186,17 +134,15 @@
@Override
public void onAlphaChanged(float alpha) {
- mNotificationPanelAlpha = (int) (alpha * 255);
- postInvalidate();
+ mAnimationView.onAlphaChanged(alpha);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mSensorRect.set(0, 0, 2 * mSensorProps.sensorRadius, 2 * mSensorProps.sensorRadius);
- if (mUdfpsAnimation != null) {
- mUdfpsAnimation.onSensorRectUpdated(new RectF(mSensorRect));
- }
+ mHbmSurfaceView.onSensorRectUpdated(new RectF(mSensorRect));
+ mAnimationView.onSensorRectUpdated(new RectF(mSensorRect));
}
@Override
@@ -205,13 +151,7 @@
Log.v(TAG, "onAttachedToWindow");
// Retrieve the colors each time, since it depends on day/night mode
- updateColor();
- }
-
- private void updateColor() {
- if (mUdfpsAnimation != null) {
- mUdfpsAnimation.updateColor();
- }
+ mAnimationView.updateColor();
}
@Override
@@ -220,25 +160,6 @@
Log.v(TAG, "onDetachedFromWindow");
}
- /**
- * Immediately draws the provided drawable on this SurfaceView's surface.
- */
- private void drawImmediately(@NonNull SimpleDrawable drawable) {
- Canvas canvas = null;
- try {
- canvas = mHolder.lockCanvas();
- drawable.draw(canvas);
- } finally {
- // Make sure the surface is never left in a bad state.
- if (canvas != null) {
- mHolder.unlockCanvasAndPost(canvas);
- }
- }
- }
-
- /**
- * This onDraw will not execute if setWillNotDraw(true) is called.
- */
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
@@ -246,11 +167,6 @@
if (!TextUtils.isEmpty(mDebugMessage)) {
canvas.drawText(mDebugMessage, 0, 160, mDebugTextPaint);
}
- if (mUdfpsAnimation != null) {
- final int alpha = shouldPauseAuth() ? 255 - mNotificationPanelAlpha : 255;
- mUdfpsAnimation.setAlpha(alpha);
- mUdfpsAnimation.draw(canvas);
- }
}
}
@@ -283,7 +199,7 @@
* authentication which would cause the UDFPS icons to abruptly disappear, do it here by not
* sending onFingerDown and smoothly animating away.
*/
- private boolean shouldPauseAuth() {
+ boolean shouldPauseAuth() {
return (mNotificationShadeExpanded && mStatusBarState != KEYGUARD)
|| mStatusBarState == SHADE_LOCKED
|| mStatusBarState == FULLSCREEN_USER_SWITCHER;
@@ -293,49 +209,30 @@
return mIlluminationRequested;
}
- void startIllumination() {
+ /**
+ * @param onIlluminatedRunnable Runs when the first illumination frame reaches the panel.
+ */
+ @Override
+ public void startIllumination(@Nullable Runnable onIlluminatedRunnable) {
mIlluminationRequested = true;
-
- // Disable onDraw to prevent overriding the illumination dot with the regular UI.
- setWillNotDraw(true);
-
- if (mHbmCallback != null && mHolder.getSurface().isValid()) {
- mHbmCallback.enableHbm(mHolder.getSurface());
- }
- drawImmediately(mIlluminationDotDrawable);
-
- if (mOnIlluminatedRunnable != null) {
- // No framework API can reliably tell when a frame reaches the panel. A timeout is the
- // safest solution. The frame should be displayed within 3 refresh cycles, which on a
- // 60 Hz panel equates to 50 milliseconds.
- postDelayed(mOnIlluminatedRunnable, 50 /* delayMillis */);
- mOnIlluminatedRunnable = null;
- }
+ mAnimationView.setVisibility(View.INVISIBLE);
+ mHbmSurfaceView.setVisibility(View.VISIBLE);
+ mHbmSurfaceView.startIllumination(onIlluminatedRunnable);
}
- void stopIllumination() {
+ @Override
+ public void stopIllumination() {
mIlluminationRequested = false;
-
- if (mHbmCallback != null && mHolder.getSurface().isValid()) {
- mHbmCallback.disableHbm(mHolder.getSurface());
- }
- // It may be necessary to clear the surface for the HBM changes to apply.
- drawImmediately(mClearSurfaceDrawable);
-
- // Enable onDraw to allow the regular UI to be drawn.
- setWillNotDraw(false);
- invalidate();
+ mAnimationView.setVisibility(View.VISIBLE);
+ mHbmSurfaceView.setVisibility(View.INVISIBLE);
+ mHbmSurfaceView.stopIllumination();
}
void onEnrollmentProgress(int remaining) {
- if (mUdfpsAnimation instanceof UdfpsAnimationEnroll) {
- ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentProgress(remaining);
- }
+ mAnimationView.onEnrollmentProgress(remaining);
}
void onEnrollmentHelp() {
- if (mUdfpsAnimation instanceof UdfpsAnimationEnroll) {
- ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentHelp();
- }
+ mAnimationView.onEnrollmentHelp();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index fcb5da3..4384610 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -545,6 +545,7 @@
}
mNavigationBarView.setNavigationIconHints(mNavigationIconHints);
mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
+ mNavigationBarView.setBehavior(mBehavior);
mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener);
mPipOptional.ifPresent(mNavigationBarView::registerPipExclusionBoundsChangeListener);
@@ -919,6 +920,9 @@
}
if (mBehavior != behavior) {
mBehavior = behavior;
+ if (mNavigationBarView != null) {
+ mNavigationBarView.setBehavior(behavior);
+ }
updateSystemUiStateFlags(-1);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index e7f2b222..c07404c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -57,6 +57,7 @@
import android.view.ViewTreeObserver.InternalInsetsInfo;
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
import android.view.WindowInsets;
+import android.view.WindowInsetsController.Behavior;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
@@ -626,6 +627,10 @@
mRotationButtonController.onNavigationBarWindowVisibilityChange(visible);
}
+ public void setBehavior(@Behavior int behavior) {
+ mRotationButtonController.onBehaviorChanged(behavior);
+ }
+
@Override
public void setLayoutDirection(int layoutDirection) {
reloadNavIcons();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
index df9e7a4..33d1807 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
@@ -35,6 +35,8 @@
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
+import android.view.WindowInsetsController;
+import android.view.WindowInsetsController.Behavior;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
@@ -78,6 +80,7 @@
private Consumer<Integer> mRotWatcherListener;
private boolean mListenersRegistered = false;
private boolean mIsNavigationBarShowing;
+ private @Behavior int mBehavior = WindowInsetsController.BEHAVIOR_DEFAULT;
private boolean mSkipOverrideUserLockPrefsOnce;
private int mLightIconColor;
private int mDarkIconColor;
@@ -297,8 +300,8 @@
}
mRotationButton.updateIcon(mLightIconColor, mDarkIconColor);
- if (mIsNavigationBarShowing) {
- // The navbar is visible so show the icon right away
+ if (canShowRotationButton()) {
+ // The navbar is visible / it's in visual immersive mode, so show the icon right away
showAndLogRotationSuggestion();
} else {
// If the navbar isn't shown, flag the rotate icon to be shown should the navbar become
@@ -318,14 +321,28 @@
void onNavigationBarWindowVisibilityChange(boolean showing) {
if (mIsNavigationBarShowing != showing) {
mIsNavigationBarShowing = showing;
-
- // If the navbar is visible, show the rotate button if there's a pending suggestion
- if (showing && mPendingRotationSuggestion) {
- showAndLogRotationSuggestion();
- }
+ showPendingRotationButtonIfNeeded();
}
}
+ void onBehaviorChanged(@Behavior int behavior) {
+ if (mBehavior != behavior) {
+ mBehavior = behavior;
+ showPendingRotationButtonIfNeeded();
+ }
+ }
+
+ private void showPendingRotationButtonIfNeeded() {
+ if (canShowRotationButton() && mPendingRotationSuggestion) {
+ showAndLogRotationSuggestion();
+ }
+ }
+
+ /** Return true when either the nav bar is visible or it's in visual immersive mode. */
+ private boolean canShowRotationButton() {
+ return mIsNavigationBarShowing || mBehavior == WindowInsetsController.BEHAVIOR_DEFAULT;
+ }
+
public Context getContext() {
return mContext;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
index b6e07b1..3841dac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
@@ -36,6 +36,7 @@
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import javax.inject.Inject;
@@ -49,9 +50,10 @@
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
- IndividualSensorPrivacyController sensorPrivacyController) {
+ IndividualSensorPrivacyController sensorPrivacyController,
+ KeyguardStateController keyguardStateController) {
super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger, sensorPrivacyController);
+ activityStarter, qsLogger, sensorPrivacyController, keyguardStateController);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
index 9cc6f09..2f0071a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
@@ -36,6 +36,7 @@
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import javax.inject.Inject;
@@ -49,9 +50,10 @@
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
- IndividualSensorPrivacyController sensorPrivacyController) {
+ IndividualSensorPrivacyController sensorPrivacyController,
+ KeyguardStateController keyguardStateController) {
super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger, sensorPrivacyController);
+ activityStarter, qsLogger, sensorPrivacyController, keyguardStateController);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
index 12205d6..00703e7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -35,6 +35,7 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
/**
* Superclass to toggle individual sensor privacy via quick settings tiles
@@ -42,6 +43,7 @@
public abstract class SensorPrivacyToggleTile extends QSTileImpl<QSTile.BooleanState> implements
IndividualSensorPrivacyController.Callback {
+ private final KeyguardStateController mKeyguard;
private IndividualSensorPrivacyController mSensorPrivacyController;
/**
@@ -61,10 +63,12 @@
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
- IndividualSensorPrivacyController sensorPrivacyController) {
+ IndividualSensorPrivacyController sensorPrivacyController,
+ KeyguardStateController keyguardStateController) {
super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
activityStarter, qsLogger);
mSensorPrivacyController = sensorPrivacyController;
+ mKeyguard = keyguardStateController;
mSensorPrivacyController.observe(getLifecycle(), this);
}
@@ -75,6 +79,13 @@
@Override
protected void handleClick() {
+ if (mKeyguard.isMethodSecure() && mKeyguard.isShowing()) {
+ mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
+ mSensorPrivacyController.setSensorBlocked(getSensorId(),
+ !mSensorPrivacyController.isSensorBlocked(getSensorId()));
+ });
+ return;
+ }
mSensorPrivacyController.setSensorBlocked(getSensorId(),
!mSensorPrivacyController.isSensorBlocked(getSensorId()));
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
new file mode 100644
index 0000000..8e182b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
@@ -0,0 +1,151 @@
+/*
+ * 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.screenshot;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.util.MathUtils;
+import android.view.MotionEvent;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+
+/**
+ * CropView has top and bottom draggable crop handles, with a scrim to darken the areas being
+ * cropped out.
+ */
+public class CropView extends View {
+ private enum CropBoundary {
+ NONE, TOP, BOTTOM
+ }
+
+ private final float mCropTouchMargin;
+ private final Paint mShadePaint;
+ private final Paint mHandlePaint;
+
+ // Top and bottom crops are stored as floats [0, 1], representing the top and bottom of the
+ // view, respectively.
+ private float mTopCrop = 0f;
+ private float mBottomCrop = 1f;
+
+ private CropBoundary mCurrentDraggingBoundary = CropBoundary.NONE;
+ private float mLastY;
+
+ public CropView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public CropView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ TypedArray t = context.getTheme().obtainStyledAttributes(
+ attrs, R.styleable.CropView, 0, 0);
+ mShadePaint = new Paint();
+ mShadePaint.setColor(t.getColor(R.styleable.CropView_scrimColor, Color.TRANSPARENT));
+ mHandlePaint = new Paint();
+ mHandlePaint.setColor(t.getColor(R.styleable.CropView_handleColor, Color.BLACK));
+ mHandlePaint.setStrokeWidth(
+ t.getDimensionPixelSize(R.styleable.CropView_handleThickness, 20));
+ t.recycle();
+ // 48 dp touchable region around each handle.
+ mCropTouchMargin = 24 * getResources().getDisplayMetrics().density;
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ drawShade(canvas, 0, mTopCrop);
+ drawShade(canvas, mBottomCrop, 1f);
+ drawHandle(canvas, mTopCrop);
+ drawHandle(canvas, mBottomCrop);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ int topPx = fractionToPixels(mTopCrop);
+ int bottomPx = fractionToPixels(mBottomCrop);
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ mCurrentDraggingBoundary = nearestBoundary(event, topPx, bottomPx);
+ if (mCurrentDraggingBoundary != CropBoundary.NONE) {
+ mLastY = event.getY();
+ }
+ return true;
+ }
+ if (event.getAction() == MotionEvent.ACTION_MOVE
+ && mCurrentDraggingBoundary != CropBoundary.NONE) {
+ float delta = event.getY() - mLastY;
+ if (mCurrentDraggingBoundary == CropBoundary.TOP) {
+ mTopCrop = pixelsToFraction((int) MathUtils.constrain(topPx + delta, 0,
+ bottomPx - 2 * mCropTouchMargin));
+ } else { // Bottom
+ mBottomCrop = pixelsToFraction((int) MathUtils.constrain(bottomPx + delta,
+ topPx + 2 * mCropTouchMargin, getMeasuredHeight()));
+ }
+ mLastY = event.getY();
+ invalidate();
+ return true;
+ }
+ return super.onTouchEvent(event);
+ }
+
+ /**
+ * @return value [0,1] representing the position of the top crop boundary.
+ */
+ public float getTopBoundary() {
+ return mTopCrop;
+ }
+
+ /**
+ * @return value [0,1] representing the position of the bottom crop boundary.
+ */
+ public float getBottomBoundary() {
+ return mBottomCrop;
+ }
+
+ private void drawShade(Canvas canvas, float fracStart, float fracEnd) {
+ canvas.drawRect(0, fractionToPixels(fracStart), getMeasuredWidth(),
+ fractionToPixels(fracEnd), mShadePaint);
+ }
+
+ private void drawHandle(Canvas canvas, float frac) {
+ int y = fractionToPixels(frac);
+ canvas.drawLine(0, y, getMeasuredWidth(), y, mHandlePaint);
+ }
+
+ private int fractionToPixels(float frac) {
+ return (int) (frac * getMeasuredHeight());
+ }
+
+ private float pixelsToFraction(int px) {
+ return px / (float) getMeasuredHeight();
+ }
+
+ private CropBoundary nearestBoundary(MotionEvent event, int topPx, int bottomPx) {
+ if (Math.abs(event.getY() - topPx) < mCropTouchMargin) {
+ return CropBoundary.TOP;
+ }
+ if (Math.abs(event.getY() - bottomPx) < mCropTouchMargin) {
+ return CropBoundary.BOTTOM;
+ }
+ return CropBoundary.NONE;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index 176a2c7..fd7db4b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -18,10 +18,12 @@
import android.annotation.IdRes;
import android.annotation.UiThread;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver.InternalInsetsInfo;
@@ -168,19 +170,20 @@
}
private void edit() {
- sendIntentWhenReady(Intent.ACTION_EDIT);
+ String editorPackage = mContext.getString(R.string.config_screenshotEditor);
+ sendIntentWhenReady(Intent.ACTION_EDIT, editorPackage);
}
private void share() {
- sendIntentWhenReady(Intent.ACTION_SEND);
+ sendIntentWhenReady(Intent.ACTION_SEND, null);
}
- void sendIntentWhenReady(String action) {
+ void sendIntentWhenReady(String action, String component) {
if (mExportFuture != null) {
mExportFuture.addListener(() -> {
try {
ImageExporter.Result result = mExportFuture.get();
- sendIntent(action, result.uri);
+ sendIntent(action, component, result.uri);
mCallback.onFinish();
} catch (InterruptedException | ExecutionException e) {
Log.e(TAG, "failed to export", e);
@@ -254,12 +257,16 @@
}
}
- void sendIntent(String action, Uri uri) {
- Intent editIntent = new Intent(action);
- editIntent.setType("image/png");
- editIntent.setData(uri);
- editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- mContext.startActivityAsUser(editIntent, UserHandle.CURRENT);
+ void sendIntent(String action, String component, Uri uri) {
+ Intent intent = new Intent(action);
+ if (!TextUtils.isEmpty(component)) {
+ intent.setComponent(ComponentName.unflattenFromString(component));
+ }
+ intent.setType("image/png");
+ intent.setData(uri);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index c1161f1..d707bca0 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -17,14 +17,19 @@
package com.android.systemui.sensorprivacy
import android.app.AppOpsManager
+import android.app.KeyguardManager
+import android.app.KeyguardManager.KeyguardDismissCallback
import android.content.DialogInterface
import android.content.Intent.EXTRA_PACKAGE_NAME
import android.content.pm.PackageManager
import android.content.res.Resources
import android.hardware.SensorPrivacyManager
-import android.hardware.SensorPrivacyManager.*
+import android.hardware.SensorPrivacyManager.EXTRA_SENSOR
+import android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA
+import android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE
import android.os.Bundle
import android.text.Html
+import android.util.Log
import com.android.internal.app.AlertActivity
import com.android.systemui.R
@@ -35,18 +40,29 @@
* <p>The dialog is started for the user the app is running for which might be a secondary users.
*/
class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListener {
+
+ companion object {
+ private val LOG_TAG = SensorUseStartedActivity::class.java.simpleName
+ }
+
private var sensor = -1
private lateinit var sensorUsePackageName: String
private lateinit var sensorPrivacyManager: SensorPrivacyManager
private lateinit var appOpsManager: AppOpsManager
+ private lateinit var keyguardManager: KeyguardManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ setShowWhenLocked(true)
+
+ setFinishOnTouchOutside(false)
+
setResult(RESULT_CANCELED)
sensorPrivacyManager = getSystemService(SensorPrivacyManager::class.java)!!
appOpsManager = getSystemService(AppOpsManager::class.java)!!
+ keyguardManager = getSystemService(KeyguardManager::class.java)!!
sensorUsePackageName = intent.getStringExtra(EXTRA_PACKAGE_NAME) ?: return
sensor = intent.getIntExtra(EXTRA_SENSOR, -1).also {
@@ -107,8 +123,23 @@
override fun onClick(dialog: DialogInterface?, which: Int) {
when (which) {
BUTTON_POSITIVE -> {
- sensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(sensor, false)
- setResult(RESULT_OK)
+ if (keyguardManager.isDeviceLocked) {
+ keyguardManager
+ .requestDismissKeyguard(this, object : KeyguardDismissCallback() {
+ override fun onDismissError() {
+ Log.e(LOG_TAG, "Cannot dismiss keyguard")
+ }
+
+ override fun onDismissSucceeded() {
+ sensorPrivacyManager
+ .setIndividualSensorPrivacyForProfileGroup(sensor, false)
+ setResult(RESULT_OK)
+ }
+ })
+ } else {
+ sensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(sensor, false)
+ setResult(RESULT_OK)
+ }
}
}
@@ -120,4 +151,8 @@
sensorPrivacyManager.suppressIndividualSensorPrivacyReminders(sensorUsePackageName, false)
}
+
+ override fun onBackPressed() {
+ // do not allow backing out
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index c2e4e14..b7aa907 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -71,6 +71,8 @@
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.phone.PipAppOpsListener;
import com.android.wm.shell.pip.phone.PipTouchHandler;
+import com.android.wm.shell.sizecompatui.SizeCompatUI;
+import com.android.wm.shell.sizecompatui.SizeCompatUIController;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
@@ -293,8 +295,8 @@
@WMSingleton
@Provides
static ShellTaskOrganizer provideShellTaskOrganizer(@ShellMainThread ShellExecutor mainExecutor,
- Context context) {
- return new ShellTaskOrganizer(mainExecutor, context);
+ Context context, SizeCompatUI sizeCompatUI) {
+ return new ShellTaskOrganizer(mainExecutor, context, sizeCompatUI);
}
@WMSingleton
@@ -380,8 +382,7 @@
@WMSingleton
@Provides
- static FullscreenTaskListener provideFullscreenTaskListener(
- SyncTransactionQueue syncQueue) {
+ static FullscreenTaskListener provideFullscreenTaskListener(SyncTransactionQueue syncQueue) {
return new FullscreenTaskListener(syncQueue);
}
@@ -392,4 +393,12 @@
@ShellAnimationThread ShellExecutor animExecutor) {
return new Transitions(organizer, pool, mainExecutor, animExecutor);
}
+
+ @WMSingleton
+ @Provides
+ static SizeCompatUI provideSizeCompatUI(Context context, DisplayController displayController,
+ DisplayImeController imeController, @ShellMainThread ShellExecutor mainExecutor) {
+ return SizeCompatUIController.create(context, displayController, imeController,
+ mainExecutor);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java
deleted file mode 100644
index 71a0434..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2019 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;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.inputmethodservice.InputMethodService;
-import android.os.IBinder;
-import android.testing.AndroidTestingRunner;
-import android.view.View;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SizeCompatModeActivityController.RestartActivityButton;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.shared.system.TaskStackChangeListeners;
-import com.android.systemui.statusbar.CommandQueue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * runtest systemui -c com.android.systemui.SizeCompatModeActivityControllerTest
- */
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class SizeCompatModeActivityControllerTest extends SysuiTestCase {
- private static final int DISPLAY_ID = 0;
-
- private SizeCompatModeActivityController mController;
- private TaskStackChangeListener mTaskStackListener;
- private @Mock TaskStackChangeListeners mMockTaskListeners;
- private @Mock RestartActivityButton mMockButton;
- private @Mock IBinder mMockActivityToken;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- doReturn(true).when(mMockButton).show();
-
- mController = new SizeCompatModeActivityController(mContext, mMockTaskListeners,
- new CommandQueue(mContext)) {
- @Override
- RestartActivityButton createRestartButton(Context context) {
- return mMockButton;
- };
- };
-
- ArgumentCaptor<TaskStackChangeListener> listenerCaptor =
- ArgumentCaptor.forClass(TaskStackChangeListener.class);
- verify(mMockTaskListeners).registerTaskStackListener(listenerCaptor.capture());
- mTaskStackListener = listenerCaptor.getValue();
- }
-
- @Test
- public void testOnSizeCompatModeActivityChanged() {
- // Verifies that the restart button is added with non-null component name.
- mTaskStackListener.onSizeCompatModeActivityChanged(DISPLAY_ID, mMockActivityToken);
- verify(mMockButton).show();
- verify(mMockButton).updateLastTargetActivity(eq(mMockActivityToken));
-
- // Verifies that the restart button is removed with null component name.
- mTaskStackListener.onSizeCompatModeActivityChanged(DISPLAY_ID, null /* activityToken */);
- verify(mMockButton).remove();
- }
-
- @Test
- public void testChangeButtonVisibilityOnImeShowHide() {
- mTaskStackListener.onSizeCompatModeActivityChanged(DISPLAY_ID, mMockActivityToken);
-
- // Verifies that the restart button is hidden when IME is visible.
- doReturn(View.VISIBLE).when(mMockButton).getVisibility();
- mController.setImeWindowStatus(DISPLAY_ID, null /* token */, InputMethodService.IME_VISIBLE,
- 0 /* backDisposition */, false /* showImeSwitcher */);
- verify(mMockButton).setVisibility(eq(View.GONE));
-
- // Verifies that the restart button is visible when IME is hidden.
- doReturn(View.GONE).when(mMockButton).getVisibility();
- mController.setImeWindowStatus(DISPLAY_ID, null /* token */, 0 /* vis */,
- 0 /* backDisposition */, false /* showImeSwitcher */);
- verify(mMockButton).setVisibility(eq(View.VISIBLE));
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 72dd442..1e473cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -188,9 +188,8 @@
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
event.recycle();
// THEN illumination begins
- verify(mUdfpsView).startIllumination();
// AND onIlluminatedRunnable that notifies FingerprintManager is set
- verify(mUdfpsView).setOnIlluminatedRunnable(mOnIlluminatedRunnableCaptor.capture());
+ verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture());
mOnIlluminatedRunnableCaptor.getValue().run();
verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mSensorProps.sensorId), eq(0),
eq(0), eq(0f), eq(0f));
@@ -205,9 +204,8 @@
// WHEN fingerprint is requested because of AOD interrupt
mUdfpsController.onAodInterrupt(0, 0, 2f, 3f);
// THEN illumination begins
- verify(mUdfpsView).startIllumination();
// AND onIlluminatedRunnable that notifies FingerprintManager is set
- verify(mUdfpsView).setOnIlluminatedRunnable(mOnIlluminatedRunnableCaptor.capture());
+ verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture());
mOnIlluminatedRunnableCaptor.getValue().run();
verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mSensorProps.sensorId), eq(0),
eq(0), eq(3f) /* minor */, eq(2f) /* major */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
index 51cf501..47f4183 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
@@ -23,6 +23,7 @@
import static org.mockito.Mockito.verify;
import android.view.View;
+import android.view.WindowInsetsController;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -87,6 +88,8 @@
@Test
public void testOnRotationProposalShowButtonShowNav() {
// No navigation bar should not call to set visibility state
+ mRotationButtonController.onBehaviorChanged(
+ WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
mRotationButtonController.onNavigationBarWindowVisibilityChange(false /* showing */);
verify(mRotationButtonController, times(0)).setRotateSuggestionButtonState(
false /* visible */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 4078d4d..79641db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -18,7 +18,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.test.suitebuilder.annotation.SmallTest;
@@ -41,7 +40,6 @@
import com.android.wm.shell.onehanded.OneHandedGestureHandler;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.Pip;
-import com.android.wm.shell.pip.phone.PipTouchHandler;
import org.junit.Before;
import org.junit.Test;
@@ -51,6 +49,12 @@
import java.util.Optional;
+/**
+ * Tests for {@link WMShell}.
+ *
+ * Build/Install/Run:
+ * atest SystemUITests:WMShellTest
+ */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class WMShellTest extends SysuiTestCase {
diff --git a/services/core/java/com/android/server/display/DisplayGroup.java b/services/core/java/com/android/server/display/DisplayGroup.java
index a11a745..663883a 100644
--- a/services/core/java/com/android/server/display/DisplayGroup.java
+++ b/services/core/java/com/android/server/display/DisplayGroup.java
@@ -21,7 +21,8 @@
/**
* Represents a collection of {@link LogicalDisplay}s which act in unison for certain behaviors and
- * operations.
+ * operations; particularly display-state.
+ *
* @hide
*/
public class DisplayGroup {
@@ -33,17 +34,44 @@
mGroupId = groupId;
}
+ /** Returns the identifier for the Group. */
int getGroupId() {
return mGroupId;
}
- void addDisplay(LogicalDisplay display) {
+ /**
+ * Adds the provided {@code display} to the Group
+ *
+ * @param display the {@link LogicalDisplay} to add to the Group
+ */
+ void addDisplayLocked(LogicalDisplay display) {
if (!mDisplays.contains(display)) {
mDisplays.add(display);
}
}
- boolean removeDisplay(LogicalDisplay display) {
+ /**
+ * Removes the provided {@code display} from the Group.
+ *
+ * @param display The {@link LogicalDisplay} to remove from the Group.
+ * @return {@code true} if the {@code display} was removed; otherwise {@code false}
+ */
+ boolean removeDisplayLocked(LogicalDisplay display) {
return mDisplays.remove(display);
}
+
+ /** Returns {@code true} if there are no {@link LogicalDisplay LogicalDisplays} in the Group. */
+ boolean isEmptyLocked() {
+ return mDisplays.isEmpty();
+ }
+
+ /** Returns the number of {@link LogicalDisplay LogicalDisplays} in the Group. */
+ int getSizeLocked() {
+ return mDisplays.size();
+ }
+
+ /** Returns the ID of the {@link LogicalDisplay} at the provided {@code index}. */
+ int getIdLocked(int index) {
+ return mDisplays.get(index).getDisplayIdLocked();
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c3f8d8c..e0baee7 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -57,6 +57,7 @@
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayGroupListener;
import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
import android.hardware.display.DisplayViewport;
import android.hardware.display.DisplayedContentSample;
@@ -190,6 +191,7 @@
private static final int MSG_UPDATE_VIEWPORT = 5;
private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATION = 6;
private static final int MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE = 7;
+ private static final int MSG_DELIVER_DISPLAY_GROUP_EVENT = 8;
private final Context mContext;
private final DisplayManagerHandler mHandler;
@@ -237,6 +239,10 @@
private final CopyOnWriteArrayList<DisplayTransactionListener> mDisplayTransactionListeners =
new CopyOnWriteArrayList<DisplayTransactionListener>();
+ /** List of all display group listeners. */
+ private final CopyOnWriteArrayList<DisplayGroupListener> mDisplayGroupListeners =
+ new CopyOnWriteArrayList<>();
+
/** All {@link DisplayPowerController}s indexed by {@link LogicalDisplay} ID. */
private final SparseArray<DisplayPowerController> mDisplayPowerControllers =
new SparseArray<>();
@@ -1677,6 +1683,11 @@
mHandler.sendMessage(msg);
}
+ private void sendDisplayGroupEvent(int groupId, int event) {
+ Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_GROUP_EVENT, groupId, event);
+ mHandler.sendMessage(msg);
+ }
+
private void sendDisplayEventFrameRateOverrideLocked(int displayId) {
Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE,
displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
@@ -1721,6 +1732,35 @@
mTempCallbacks.clear();
}
+ // Runs on Handler thread.
+ // Delivers display group event notifications to callbacks.
+ private void deliverDisplayGroupEvent(int groupId, int event) {
+ if (DEBUG) {
+ Slog.d(TAG, "Delivering display group event: groupId=" + groupId + ", event="
+ + event);
+ }
+
+ switch (event) {
+ case LogicalDisplayMapper.DISPLAY_GROUP_EVENT_ADDED:
+ for (DisplayGroupListener listener : mDisplayGroupListeners) {
+ listener.onDisplayGroupAdded(groupId);
+ }
+ break;
+
+ case LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED:
+ for (DisplayGroupListener listener : mDisplayGroupListeners) {
+ listener.onDisplayGroupChanged(groupId);
+ }
+ break;
+
+ case LogicalDisplayMapper.DISPLAY_GROUP_EVENT_REMOVED:
+ for (DisplayGroupListener listener : mDisplayGroupListeners) {
+ listener.onDisplayGroupRemoved(groupId);
+ }
+ break;
+ }
+ }
+
private IMediaProjectionManager getProjectionService() {
if (mProjectionService == null) {
IBinder b = ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE);
@@ -1943,6 +1983,11 @@
}
deliverDisplayEvent(msg.arg1, uids, msg.arg2);
break;
+
+ case MSG_DELIVER_DISPLAY_GROUP_EVENT:
+ deliverDisplayGroupEvent(msg.arg1, msg.arg2);
+ break;
+
}
}
}
@@ -1974,6 +2019,11 @@
}
@Override
+ public void onDisplayGroupEventLocked(int groupId, int event) {
+ sendDisplayGroupEvent(groupId, event);
+ }
+
+ @Override
public void onTraversalRequested() {
synchronized (mSyncRoot) {
scheduleTraversalLocked(false);
@@ -2708,11 +2758,25 @@
}
@Override
- public boolean requestPowerState(DisplayPowerRequest request,
+ public boolean requestPowerState(int groupId, DisplayPowerRequest request,
boolean waitForNegativeProximity) {
synchronized (mSyncRoot) {
- return mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY)
- .requestPowerState(request, waitForNegativeProximity);
+ final DisplayGroup displayGroup = mLogicalDisplayMapper.getDisplayGroupLocked(
+ groupId);
+ if (displayGroup == null) {
+ return true;
+ }
+
+ final int size = displayGroup.getSizeLocked();
+ boolean ready = true;
+ for (int i = 0; i < size; i++) {
+ final DisplayPowerController displayPowerController =
+ mDisplayPowerControllers.get(displayGroup.getIdLocked(i));
+ ready &= displayPowerController.requestPowerState(request,
+ waitForNegativeProximity);
+ }
+
+ return ready;
}
}
@@ -2732,6 +2796,16 @@
}
@Override
+ public void registerDisplayGroupListener(DisplayGroupListener listener) {
+ mDisplayGroupListeners.add(listener);
+ }
+
+ @Override
+ public void unregisterDisplayGroupListener(DisplayGroupListener listener) {
+ mDisplayGroupListeners.remove(listener);
+ }
+
+ @Override
public SurfaceControl.ScreenshotHardwareBuffer systemScreenshot(int displayId) {
return systemScreenshotInternal(displayId);
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index e738878..8ee01be 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -55,6 +55,10 @@
public static final int LOGICAL_DISPLAY_EVENT_SWAPPED = 4;
public static final int LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED = 5;
+ public static final int DISPLAY_GROUP_EVENT_ADDED = 1;
+ public static final int DISPLAY_GROUP_EVENT_CHANGED = 2;
+ public static final int DISPLAY_GROUP_EVENT_REMOVED = 3;
+
/**
* Temporary display info, used for comparing display configurations.
*/
@@ -99,7 +103,7 @@
private int mNextNonDefaultGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
/** A mapping from logical display id to display group. */
- private final SparseArray<DisplayGroup> mDisplayGroups = new SparseArray<>();
+ private final SparseArray<DisplayGroup> mDisplayIdToGroupMap = new SparseArray<>();
private final DisplayDeviceRepository mDisplayDeviceRepo;
private final Listener mListener;
@@ -183,7 +187,7 @@
}
public int getDisplayGroupIdLocked(int displayId) {
- final DisplayGroup displayGroup = mDisplayGroups.get(displayId);
+ final DisplayGroup displayGroup = mDisplayIdToGroupMap.get(displayId);
if (displayGroup != null) {
return displayGroup.getGroupId();
}
@@ -191,6 +195,18 @@
return -1;
}
+ public DisplayGroup getDisplayGroupLocked(int groupId) {
+ final int size = mDisplayIdToGroupMap.size();
+ for (int i = 0; i < size; i++) {
+ final DisplayGroup displayGroup = mDisplayIdToGroupMap.valueAt(i);
+ if (displayGroup.getGroupId() == groupId) {
+ return displayGroup;
+ }
+ }
+
+ return null;
+ }
+
public void dumpLocked(PrintWriter pw) {
pw.println("LogicalDisplayMapper:");
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
@@ -320,7 +336,7 @@
final int groupId = assignDisplayGroupIdLocked(isDefault);
displayGroup = new DisplayGroup(groupId);
} else {
- displayGroup = mDisplayGroups.get(Display.DEFAULT_DISPLAY);
+ displayGroup = mDisplayIdToGroupMap.get(Display.DEFAULT_DISPLAY);
}
LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
@@ -335,11 +351,23 @@
mLogicalDisplays.put(displayId, display);
- displayGroup.addDisplay(display);
- mDisplayGroups.append(displayId, displayGroup);
+ displayGroup.addDisplayLocked(display);
+ mDisplayIdToGroupMap.append(displayId, displayGroup);
+
+ if (addNewDisplayGroup) {
+ // Group added events happen before Logical Display added events.
+ mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(),
+ LogicalDisplayMapper.DISPLAY_GROUP_EVENT_ADDED);
+ }
mListener.onLogicalDisplayEventLocked(display,
LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED);
+
+ if (!addNewDisplayGroup) {
+ // Group changed events happen after Logical Display added events.
+ mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(),
+ LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED);
+ }
}
/**
@@ -356,33 +384,47 @@
DisplayEventReceiver.FrameRateOverride[] frameRatesOverrides =
display.getFrameRateOverrides();
display.updateLocked(mDisplayDeviceRepo);
+ final DisplayGroup changedDisplayGroup;
if (!display.isValidLocked()) {
mLogicalDisplays.removeAt(i);
- mDisplayGroups.removeReturnOld(displayId).removeDisplay(display);
+ final DisplayGroup displayGroup = mDisplayIdToGroupMap.removeReturnOld(displayId);
+ displayGroup.removeDisplayLocked(display);
mListener.onLogicalDisplayEventLocked(display,
LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED);
+
+ changedDisplayGroup = displayGroup;
} else if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) {
final int flags = display.getDisplayInfoLocked().flags;
- final DisplayGroup defaultDisplayGroup = mDisplayGroups.get(
+ final DisplayGroup defaultDisplayGroup = mDisplayIdToGroupMap.get(
Display.DEFAULT_DISPLAY);
if ((flags & Display.FLAG_OWN_DISPLAY_GROUP) != 0) {
// The display should have its own DisplayGroup.
- if (defaultDisplayGroup.removeDisplay(display)) {
+ if (defaultDisplayGroup.removeDisplayLocked(display)) {
final int groupId = assignDisplayGroupIdLocked(false);
final DisplayGroup displayGroup = new DisplayGroup(groupId);
- displayGroup.addDisplay(display);
- mDisplayGroups.append(display.getDisplayIdLocked(), displayGroup);
+ displayGroup.addDisplayLocked(display);
display.updateDisplayGroupIdLocked(groupId);
+ mDisplayIdToGroupMap.append(display.getDisplayIdLocked(), displayGroup);
+ mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(),
+ LogicalDisplayMapper.DISPLAY_GROUP_EVENT_ADDED);
+ changedDisplayGroup = defaultDisplayGroup;
+ } else {
+ changedDisplayGroup = null;
}
} else {
// The display should be a part of the default DisplayGroup.
- final DisplayGroup displayGroup = mDisplayGroups.get(displayId);
+ final DisplayGroup displayGroup = mDisplayIdToGroupMap.get(displayId);
if (displayGroup != defaultDisplayGroup) {
- displayGroup.removeDisplay(display);
- defaultDisplayGroup.addDisplay(display);
- mDisplayGroups.put(displayId, defaultDisplayGroup);
+ displayGroup.removeDisplayLocked(display);
+ defaultDisplayGroup.addDisplayLocked(display);
display.updateDisplayGroupIdLocked(defaultDisplayGroup.getGroupId());
+ mListener.onDisplayGroupEventLocked(defaultDisplayGroup.getGroupId(),
+ LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED);
+ mDisplayIdToGroupMap.put(displayId, defaultDisplayGroup);
+ changedDisplayGroup = displayGroup;
+ } else {
+ changedDisplayGroup = null;
}
}
@@ -394,6 +436,7 @@
} else if (!display.getPendingFrameRateOverrideUids().isEmpty()) {
mListener.onLogicalDisplayEventLocked(display,
LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
+ changedDisplayGroup = null;
} else {
// While applications shouldn't know nor care about the non-overridden info, we
// still need to let WindowManager know so it can update its own internal state for
@@ -403,6 +446,15 @@
mListener.onLogicalDisplayEventLocked(display,
LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_CHANGED);
}
+ changedDisplayGroup = null;
+ }
+
+ // CHANGED and REMOVED DisplayGroup events should always fire after Display events.
+ if (changedDisplayGroup != null) {
+ final int event = changedDisplayGroup.isEmptyLocked()
+ ? LogicalDisplayMapper.DISPLAY_GROUP_EVENT_REMOVED
+ : LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED;
+ mListener.onDisplayGroupEventLocked(changedDisplayGroup.getGroupId(), event);
}
}
}
@@ -438,6 +490,7 @@
public interface Listener {
void onLogicalDisplayEventLocked(LogicalDisplay display, int event);
+ void onDisplayGroupEventLocked(int groupId, int event);
void onTraversalRequested();
}
}
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
index d2111e7..2029f39 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
@@ -169,8 +169,9 @@
sb.append(c++);
sb.append("]: lang=\"");
sb.append(family.getLocaleList().toLanguageTags());
+ sb.append("\"");
if (family.getVariant() != FontConfig.FontFamily.VARIANT_DEFAULT) {
- sb.append("\", variant=");
+ sb.append(", variant=");
switch (family.getVariant()) {
case FontConfig.FontFamily.VARIANT_COMPACT:
sb.append("Compact");
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 61c8b17..461d519 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1419,8 +1419,10 @@
conversation.setPkg(p.pkg);
conversation.setUid(p.uid);
conversation.setNotificationChannel(nc);
- conversation.setParentChannelLabel(
- p.channels.get(nc.getParentChannelId()).getName());
+ NotificationChannel parent = p.channels.get(nc.getParentChannelId());
+ conversation.setParentChannelLabel(parent == null
+ ? null
+ : parent.getName());
boolean blockedByGroup = false;
if (nc.getGroup() != null) {
NotificationChannelGroup group = p.groups.get(nc.getGroup());
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 3207d56a..212edf6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -122,6 +122,7 @@
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URISyntaxException;
+import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collection;
@@ -155,6 +156,8 @@
boolean mComponents;
int mQueryFlags;
+ private static final SecureRandom RANDOM = new SecureRandom();
+
PackageManagerShellCommand(PackageManagerService service, Context context) {
mInterface = service;
mLegacyPermissionManager = LocalServices.getService(LegacyPermissionManagerInternal.class);
@@ -3146,7 +3149,7 @@
// 1. Single file from stdin.
if (args.isEmpty() || STDIN_PATH.equals(args.get(0))) {
- final String name = "base." + (isApex ? "apex" : "apk");
+ final String name = "base" + RANDOM.nextInt() + "." + (isApex ? "apex" : "apk");
final Metadata metadata = Metadata.forStdIn(name);
session.addFile(LOCATION_DATA_APP, name, sessionSizeBytes,
metadata.toByteArray(), null);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 2929568..349d556 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4881,7 +4881,7 @@
}
if (ps.sharedUser == null || permissionNames != null || dumpAll) {
- dumpInstallPermissionsLPr(pw, prefix + " ", permissionNames, permissionsState);
+ dumpInstallPermissionsLPr(pw, prefix + " ", permissionNames, permissionsState, users);
}
if (dumpAllComponents) {
@@ -5131,9 +5131,12 @@
continue;
}
- dumpInstallPermissionsLPr(pw, prefix, permissionNames, permissionsState);
+ List<UserInfo> users = getAllUsers(UserManagerService.getInstance());
- for (int userId : UserManagerService.getInstance().getUserIds()) {
+ dumpInstallPermissionsLPr(pw, prefix, permissionNames, permissionsState, users);
+
+ for (UserInfo user : users) {
+ final int userId = user.id;
final int[] gids = mPermissionDataProvider.getGidsForUid(UserHandle.getUid(
userId, su.userId));
final Collection<PermissionState> permissions =
@@ -5247,31 +5250,55 @@
}
}
- void dumpInstallPermissionsLPr(PrintWriter pw, String prefix, ArraySet<String> permissionNames,
- LegacyPermissionState permissionsState) {
- Collection<PermissionState> permissionStates = permissionsState.getPermissionStates(
- UserHandle.USER_SYSTEM);
- boolean hasInstallPermissions = false;
- for (PermissionState permissionState : permissionStates) {
- if (!permissionState.isRuntime()) {
- hasInstallPermissions = true;
- break;
- }
- }
- if (hasInstallPermissions) {
- pw.print(prefix); pw.println("install permissions:");
+ void dumpInstallPermissionsLPr(PrintWriter pw, String prefix,
+ ArraySet<String> filterPermissionNames, LegacyPermissionState permissionsState,
+ List<UserInfo> users) {
+ ArraySet<String> dumpPermissionNames = new ArraySet<>();
+ for (UserInfo user : users) {
+ int userId = user.id;
+ Collection<PermissionState> permissionStates = permissionsState.getPermissionStates(
+ userId);
for (PermissionState permissionState : permissionStates) {
if (permissionState.isRuntime()) {
continue;
}
- if (permissionNames != null
- && !permissionNames.contains(permissionState.getName())) {
+ String permissionName = permissionState.getName();
+ if (filterPermissionNames != null
+ && !filterPermissionNames.contains(permissionName)) {
continue;
}
- pw.print(prefix); pw.print(" "); pw.print(permissionState.getName());
- pw.print(": granted="); pw.print(permissionState.isGranted());
- pw.println(permissionFlagsToString(", flags=",
- permissionState.getFlags()));
+ dumpPermissionNames.add(permissionName);
+ }
+ }
+ boolean printedSomething = false;
+ for (String permissionName : dumpPermissionNames) {
+ PermissionState systemPermissionState = permissionsState.getPermissionState(
+ permissionName, UserHandle.USER_SYSTEM);
+ for (UserInfo user : users) {
+ int userId = user.id;
+ PermissionState permissionState;
+ if (userId == UserHandle.USER_SYSTEM) {
+ permissionState = systemPermissionState;
+ } else {
+ permissionState = permissionsState.getPermissionState(permissionName, userId);
+ if (Objects.equals(permissionState, systemPermissionState)) {
+ continue;
+ }
+ }
+ if (!printedSomething) {
+ pw.print(prefix); pw.println("install permissions:");
+ printedSomething = true;
+ }
+ pw.print(prefix); pw.print(" "); pw.print(permissionName);
+ pw.print(": granted="); pw.print(
+ permissionState != null && permissionState.isGranted());
+ pw.print(permissionFlagsToString(", flags=",
+ permissionState != null ? permissionState.getFlags() : 0));
+ if (userId == UserHandle.USER_SYSTEM) {
+ pw.println();
+ } else {
+ pw.print(", userId="); pw.println(userId);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
index 851ddd1..e8be9b6 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -19,6 +19,7 @@
import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityThread;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
@@ -33,6 +34,7 @@
import android.os.Build;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.permission.PermissionManager;
import android.util.DisplayMetrics;
import android.util.Slog;
@@ -42,6 +44,7 @@
import com.android.server.pm.parsing.pkg.ParsedPackage;
import java.io.File;
+import java.util.List;
/**
* The v2 of {@link PackageParser} for use when parsing is initiated in the server and must
@@ -118,10 +121,15 @@
displayMetrics.setToDefaults();
}
+ PermissionManager permissionManager = ActivityThread.currentApplication()
+ .getSystemService(PermissionManager.class);
+ List<PermissionManager.SplitPermissionInfo> splitPermissions = permissionManager
+ .getSplitPermissions();
+
mCacher = cacheDir == null ? null : new PackageCacher(cacheDir);
parsingUtils = new ParsingPackageUtils(onlyCoreApps, separateProcesses, displayMetrics,
- callback);
+ splitPermissions, callback);
ParseInput.Callback enforcementCallback = (changeId, packageName, targetSdkVersion) -> {
ApplicationInfo appInfo = mSharedAppInfo.get();
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java
index 92f22a4..e8eae47 100644
--- a/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java
@@ -104,6 +104,26 @@
}
/**
+ * Get the permission state for a permission and a user.
+ *
+ * @param permissionName the permission name
+ * @param userId the user ID
+ * @return the permission state
+ *
+ * @hide
+ */
+ @Nullable
+ public PermissionState getPermissionState(@NonNull String permissionName,
+ @UserIdInt int userId) {
+ checkUserId(userId);
+ UserState userState = mUserStates.get(userId);
+ if (userState == null) {
+ return null;
+ }
+ return userState.getPermissionState(permissionName);
+ }
+
+ /**
* Put a permission state for a user.
*
* @param permissionState the permission state
@@ -142,10 +162,10 @@
}
/**
- * Get all the runtime permission states for a user.
+ * Get all the permission states for a user.
*
* @param userId the user ID
- * @return the runtime permission states
+ * @return the permission states
*/
@NonNull
public Collection<PermissionState> getPermissionStates(@UserIdInt int userId) {
@@ -301,5 +321,25 @@
public int getFlags() {
return mFlags;
}
+
+ @Override
+ public boolean equals(@Nullable Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (object == null || getClass() != object.getClass()) {
+ return false;
+ }
+ PermissionState that = (PermissionState) object;
+ return mRuntime == that.mRuntime
+ && mGranted == that.mGranted
+ && mFlags == that.mFlags
+ && Objects.equals(mName, that.mName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mName, mRuntime, mGranted, mFlags);
+ }
}
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index db4b6d0..8e0d632 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -2892,8 +2892,8 @@
PowerManager.BRIGHTNESS_INVALID_FLOAT;
}
- mDisplayReady = mDisplayManagerInternal.requestPowerState(displayPowerRequest,
- mRequestWaitForNegativeProximity);
+ mDisplayReady = mDisplayManagerInternal.requestPowerState(Display.DEFAULT_DISPLAY_GROUP,
+ displayPowerRequest, mRequestWaitForNegativeProximity);
mRequestWaitForNegativeProximity = false;
if (DEBUG_SPEW) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index d5d06f9..35260d3 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5500,12 +5500,9 @@
// The callback is only interested in the foreground changes of fullscreen activity.
return;
}
+ // TODO(b/178327644) Update for per Task size compat
if (!r.inSizeCompatMode()) {
if (mLastCompatModeActivity != null) {
- // TODO(b/178327644) Remove notifySizeCompatModeActivityChanged
- mAtmService.getTaskChangeNotificationController()
- .notifySizeCompatModeActivityChanged(mDisplayId, null /* activityToken */);
- // This will do nothing until SizeCompatModeActivityController is moved to shell
organizedTask.onSizeCompatActivityChanged();
}
mLastCompatModeActivity = null;
@@ -5515,10 +5512,6 @@
return;
}
mLastCompatModeActivity = r;
- // TODO(b/178327644) Remove notifySizeCompatModeActivityChanged
- mAtmService.getTaskChangeNotificationController()
- .notifySizeCompatModeActivityChanged(mDisplayId, r.appToken);
- // This will do nothing until SizeCompatModeActivityController is moved to shell
organizedTask.onSizeCompatActivityChanged();
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 3d3e31d..8d8bdcb 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -22,7 +22,6 @@
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY;
-import static android.Manifest.permission.USE_BACKGROUND_BLUR;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
@@ -111,7 +110,6 @@
final boolean mCanCreateSystemApplicationOverlay;
final boolean mCanHideNonSystemOverlayWindows;
final boolean mCanAcquireSleepToken;
- final boolean mCanUseBackgroundBlur;
private AlertWindowNotification mAlertWindowNotification;
private boolean mShowingAlertWindowNotificationAllowed;
private boolean mClientDead = false;
@@ -142,8 +140,6 @@
&& !mService.mAtmInternal.isCallerRecents(mUid);
mCanAcquireSleepToken = service.mContext.checkCallingOrSelfPermission(DEVICE_POWER)
== PERMISSION_GRANTED;
- mCanUseBackgroundBlur = service.mContext.checkCallingOrSelfPermission(USE_BACKGROUND_BLUR)
- == PERMISSION_GRANTED;
mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications;
mDragDropController = mService.mDragDropController;
StringBuilder sb = new StringBuilder();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 931f529..1cc6239 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1652,7 +1652,7 @@
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], attrs, viewVisibility, session.mUid, userId,
- session.mCanAddInternalSystemWindow, session.mCanUseBackgroundBlur);
+ session.mCanAddInternalSystemWindow);
if (win.mDeathRecipient == null) {
// Client has apparently died, so there is no reason to
// continue.
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 0250376..c698591 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -45,7 +45,6 @@
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
-import static android.view.WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
@@ -297,8 +296,6 @@
final int mShowUserId;
/** The owner has {@link android.Manifest.permission#INTERNAL_SYSTEM_WINDOW} */
final boolean mOwnerCanAddInternalSystemWindow;
- /** The owner has {@link android.Manifest.permission#USE_BACKGROUND_BLUR} */
- final boolean mOwnerCanUseBackgroundBlur;
final WindowId mWindowId;
WindowToken mToken;
// The same object as mToken if this is an app window and null for non-app windows.
@@ -897,11 +894,9 @@
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,
- int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow,
- boolean ownerCanUseBackgroundBlur) {
+ int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow) {
this(service, s, c, token, parentWindow, appOp, a, viewVisibility, ownerId, showUserId,
- ownerCanAddInternalSystemWindow, ownerCanUseBackgroundBlur,
- new PowerManagerWrapper() {
+ ownerCanAddInternalSystemWindow, new PowerManagerWrapper() {
@Override
public void wakeUp(long time, @WakeReason int reason, String details) {
service.mPowerManager.wakeUp(time, reason, details);
@@ -917,7 +912,7 @@
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,
int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow,
- boolean ownerCanUseBackgroundBlur, PowerManagerWrapper powerManagerWrapper) {
+ PowerManagerWrapper powerManagerWrapper) {
super(service);
mTmpTransaction = service.mTransactionFactory.get();
mSession = s;
@@ -928,7 +923,6 @@
mOwnerUid = ownerId;
mShowUserId = showUserId;
mOwnerCanAddInternalSystemWindow = ownerCanAddInternalSystemWindow;
- mOwnerCanUseBackgroundBlur = ownerCanUseBackgroundBlur;
mWindowId = new WindowId(this);
mAttrs.copyFrom(a);
mLastSurfaceInsets.set(mAttrs.surfaceInsets);
@@ -5275,7 +5269,7 @@
if (!mAnimatingExit && mAppDied) {
mIsDimming = true;
getDimmer().dimAbove(getSyncTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW);
- } else if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || isBlurEnabled())
+ } else if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || mAttrs.backgroundBlurRadius != 0)
&& isVisibleNow() && !mHidden) {
// Only show the Dimmer when the following is satisfied:
// 1. The window has the flag FLAG_DIM_BEHIND or background blur is requested
@@ -5284,15 +5278,11 @@
// 4. The WS is not hidden.
mIsDimming = true;
final float dimAmount = (mAttrs.flags & FLAG_DIM_BEHIND) != 0 ? mAttrs.dimAmount : 0;
- final int blurRadius = isBlurEnabled() ? mAttrs.backgroundBlurRadius : 0;
- getDimmer().dimBelow(getSyncTransaction(), this, dimAmount, blurRadius);
+ getDimmer().dimBelow(
+ getSyncTransaction(), this, mAttrs.dimAmount, mAttrs.backgroundBlurRadius);
}
}
- private boolean isBlurEnabled() {
- return (mAttrs.flags & FLAG_BLUR_BEHIND) != 0 && mOwnerCanUseBackgroundBlur;
- }
-
/**
* Notifies SF about the priority of the window, if it changed. SF then uses this information
* to decide which window's desired rendering rate should have a priority when deciding about
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index 156ef79..31cc295 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -123,7 +123,7 @@
}
return -1;
}
-static bool getAnyPageAdvice(const Vma& vma) {
+static int getAnyPageAdvice(const Vma& vma) {
if (vma.inode == 0 && !vma.is_shared) {
return MADV_PAGEOUT;
}
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index 7b379e5..5a5b0a8 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -22,6 +22,7 @@
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <core_jni_helpers.h>
+#include <cutils/multiuser.h>
#include <cutils/trace.h>
#include <endian.h>
#include <nativehelper/JNIHelp.h>
@@ -375,6 +376,9 @@
}
private:
+ // Bitmask of supported features.
+ DataLoaderFeatures getFeatures() const final { return DATA_LOADER_FEATURE_UID; }
+
// Lifecycle.
bool onCreate(const android::dataloader::DataLoaderParams& params,
android::dataloader::FilesystemConnectorPtr ifs,
@@ -554,51 +558,6 @@
return true;
}
- // Read tracing.
- struct TracedRead {
- uint64_t timestampUs;
- android::dataloader::FileId fileId;
- uint32_t firstBlockIdx;
- uint32_t count;
- };
-
- void onPageReads(android::dataloader::PageReads pageReads) final {
- auto trace = atrace_is_tag_enabled(ATRACE_TAG);
- if (CC_LIKELY(!trace)) {
- return;
- }
-
- TracedRead last = {};
- for (auto&& read : pageReads) {
- if (read.id != last.fileId || read.block != last.firstBlockIdx + last.count) {
- traceRead(last);
- last = TracedRead{
- .timestampUs = read.bootClockTsUs,
- .fileId = read.id,
- .firstBlockIdx = (uint32_t)read.block,
- .count = 1,
- };
- } else {
- ++last.count;
- }
- }
- traceRead(last);
- }
-
- void traceRead(const TracedRead& read) {
- if (!read.count) {
- return;
- }
-
- FileIdx fileIdx = convertFileIdToFileIndex(read.fileId);
- auto str = android::base::StringPrintf("page_read: index=%lld count=%lld file=%d",
- static_cast<long long>(read.firstBlockIdx),
- static_cast<long long>(read.count),
- static_cast<int>(fileIdx));
- ATRACE_BEGIN(str.c_str());
- ATRACE_END();
- }
-
// Streaming.
bool initStreaming(unique_fd inout, MetadataMode mode) {
mEventFd.reset(eventfd(0, EFD_CLOEXEC));
@@ -634,7 +593,10 @@
}
// IFS callbacks.
- void onPendingReads(dataloader::PendingReads pendingReads) final {
+ void onPendingReads(dataloader::PendingReads pendingReads) final {}
+ void onPageReads(dataloader::PageReads pageReads) final {}
+
+ void onPendingReadsWithUid(dataloader::PendingReadsWithUid pendingReads) final {
std::lock_guard lock{mOutFdLock};
if (mOutFd < 0) {
return;
@@ -660,6 +622,67 @@
}
}
+ // Read tracing.
+ struct TracedRead {
+ uint64_t timestampUs;
+ android::dataloader::FileId fileId;
+ android::dataloader::Uid uid;
+ uint32_t firstBlockIdx;
+ uint32_t count;
+ };
+
+ void onPageReadsWithUid(dataloader::PageReadsWithUid pageReads) final {
+ auto trace = atrace_is_tag_enabled(ATRACE_TAG);
+ if (CC_LIKELY(!trace)) {
+ return;
+ }
+
+ TracedRead last = {};
+ for (auto&& read : pageReads) {
+ if (read.id != last.fileId || read.uid != last.uid ||
+ read.block != last.firstBlockIdx + last.count) {
+ traceRead(last);
+ last = TracedRead{
+ .timestampUs = read.bootClockTsUs,
+ .fileId = read.id,
+ .uid = read.uid,
+ .firstBlockIdx = (uint32_t)read.block,
+ .count = 1,
+ };
+ } else {
+ ++last.count;
+ }
+ }
+ traceRead(last);
+ }
+
+ void traceRead(const TracedRead& read) {
+ if (!read.count) {
+ return;
+ }
+
+ FileIdx fileIdx = convertFileIdToFileIndex(read.fileId);
+
+ std::string trace;
+ if (read.uid != kIncFsNoUid) {
+ auto appId = multiuser_get_app_id(read.uid);
+ auto userId = multiuser_get_user_id(read.uid);
+ trace = android::base::
+ StringPrintf("page_read: index=%lld count=%lld file=%d appid=%d userid=%d",
+ static_cast<long long>(read.firstBlockIdx),
+ static_cast<long long>(read.count), static_cast<int>(fileIdx),
+ static_cast<int>(appId), static_cast<int>(userId));
+ } else {
+ trace = android::base::StringPrintf("page_read: index=%lld count=%lld file=%d",
+ static_cast<long long>(read.firstBlockIdx),
+ static_cast<long long>(read.count),
+ static_cast<int>(fileIdx));
+ }
+
+ ATRACE_BEGIN(trace.c_str());
+ ATRACE_END();
+ }
+
void receiver(unique_fd inout, MetadataMode mode) {
std::vector<uint8_t> data;
std::vector<IncFsDataBlock> instructions;
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 132f973..56cb3d1 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -1091,7 +1091,7 @@
maxPendingTimeUs = std::max(maxPendingTimeUs, microseconds(timeouts.maxPendingTimeUs));
}
if (maxPendingTimeUs < Constants::minPerUidTimeout) {
- LOG(ERROR) << "Skip setting timeouts: maxPendingTime < Constants::minPerUidTimeout"
+ LOG(ERROR) << "Skip setting read timeouts (maxPendingTime < Constants::minPerUidTimeout): "
<< duration_cast<milliseconds>(maxPendingTimeUs).count() << "ms < "
<< Constants::minPerUidTimeout.count() << "ms";
return;
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 3573177..25d3f77 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -221,10 +221,6 @@
timeout.maxPendingTimeUs = perUidTimeout.maxPendingTimeUs;
}
- LOG(ERROR) << "Set read timeouts: " << timeouts.size() << " ["
- << (timeouts.empty() ? -1 : timeouts.front().uid) << "@"
- << (timeouts.empty() ? -1 : timeouts.front().minTimeUs / 1000) << "ms]";
-
return incfs::setUidReadTimeouts(control, timeouts);
}
};
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 8d744c4..9b6c723 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
@@ -700,14 +700,14 @@
public void testRemoveEmptyDatabase_noExceptionThrown() throws Exception {
SearchSpec searchSpec =
new SearchSpec.Builder()
- .addSchemaType("FakeType")
+ .addFilterSchemas("FakeType")
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
.build();
mAppSearchImpl.removeByQuery("package", "EmptyDatabase", "", searchSpec);
searchSpec =
new SearchSpec.Builder()
- .addNamespace("FakeNamespace")
+ .addFilterNamespaces("FakeNamespace")
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
.build();
mAppSearchImpl.removeByQuery("package", "EmptyDatabase", "", searchSpec);
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
index bb223b3..fb13d87 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
@@ -55,6 +55,7 @@
import java.io.File;
import java.io.InputStream;
+import java.util.Collections;
import java.util.function.Function;
/**
@@ -523,7 +524,7 @@
int flags = PackageManager.GET_META_DATA | PackageManager.GET_SIGNING_CERTIFICATES;
ParseResult<ParsingPackage> result = ParsingPackageUtils.parseDefaultOneTime(apexFile,
- flags, false /*collectCertificates*/);
+ flags, Collections.emptyList(), false /*collectCertificates*/);
if (result.isError()) {
throw new IllegalStateException(result.getErrorMessage(), result.getException());
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
index d8910de..4dc9a90 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
@@ -19,12 +19,10 @@
import android.annotation.RawRes
import android.content.Context
import android.content.pm.parsing.ParsingPackage
-import android.content.pm.parsing.ParsingPackageImpl
import android.content.pm.parsing.ParsingPackageUtils
import android.content.pm.parsing.result.ParseInput
import android.content.pm.parsing.result.ParseInput.DeferredError
import android.content.pm.parsing.result.ParseResult
-import android.content.res.TypedArray
import android.os.Build
import androidx.test.InstrumentationRegistry
import com.android.frameworks.servicestests.R
@@ -130,7 +128,7 @@
input.copyTo(output)
}
}
- return ParsingPackageUtils.parseDefaultOneTime(file, 0 /*flags*/,
+ return ParsingPackageUtils.parseDefaultOneTime(file, 0 /*flags*/, emptyList(),
false /*collectCertificates*/)
}
}
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 1d0b595..592eea1 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -179,7 +179,8 @@
.thenReturn(mPowerSaveState);
when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(false);
when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(false);
- when(mDisplayManagerInternalMock.requestPowerState(any(), anyBoolean())).thenReturn(true);
+ when(mDisplayManagerInternalMock.requestPowerState(anyInt(), any(), anyBoolean()))
+ .thenReturn(true);
when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn("");
when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 8c744c9..fc1cb70 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -3536,6 +3536,28 @@
assertFalse(conversationWrapperContainsChannel(convos, channel2));
}
+ @Test
+ public void testGetConversations_parentDeleted() {
+ String convoId = "convo";
+ NotificationChannel messages =
+ new NotificationChannel("messages", "Messages", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_O, UID_O, messages, true, false);
+
+ NotificationChannel channel =
+ new NotificationChannel("A person msgs", "messages from A", IMPORTANCE_DEFAULT);
+ channel.setConversationId(messages.getId(), convoId);
+ channel.setImportantConversation(true);
+ mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
+
+ mHelper.permanentlyDeleteNotificationChannel(PKG_O, UID_O, "messages");
+
+ List<ConversationChannelWrapper> convos =
+ mHelper.getConversations(IntArray.wrap(new int[] {0}), true);
+
+ assertEquals(1, convos.size());
+ assertTrue(conversationWrapperContainsChannel(convos, channel));
+ }
+
private boolean conversationWrapperContainsChannel(List<ConversationChannelWrapper> list,
NotificationChannel expected) {
for (ConversationChannelWrapper ccw : list) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 91fd7a2..89b962b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -541,7 +541,7 @@
return new WindowState(mWm, mock(Session.class), new TestIWindow(), token,
null /* parentWindow */, 0 /* appOp */, new WindowManager.LayoutParams(),
View.VISIBLE, 0 /* ownerId */, 0 /* showUserId */,
- false /* ownerCanAddInternalSystemWindow */, false /* ownerCanUseBackgroundBlur */);
+ false /* ownerCanAddInternalSystemWindow */);
}
private WindowToken createWindowToken(int type) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index bbd89b8..cc4d4ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -52,14 +52,12 @@
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
-import android.app.TaskStackListener;
import android.app.WindowConfiguration;
import android.compat.testing.PlatformCompatChangeRule;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
-import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.view.WindowManager;
@@ -72,8 +70,6 @@
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
-import java.util.ArrayList;
-
/**
* Tests for Size Compatibility mode.
*
@@ -479,52 +475,6 @@
}
/**
- * Ensures that {@link TaskStackListener} can receive callback about the activity in size
- * compatibility mode.
- *
- * TODO(b/178327644) Remove after update DC#handleActivitySizeCompatModeIfNeeded
- */
- @Test
- public void testHandleActivitySizeCompatMode() {
- setUpDisplaySizeWithApp(1000, 2000);
- doReturn(true).when(mTask).isOrganized();
- ActivityRecord activity = mActivity;
- activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
- prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
- assertFitted();
-
- final ArrayList<IBinder> compatTokens = new ArrayList<>();
- mAtm.getTaskChangeNotificationController().registerTaskStackListener(
- new TaskStackListener() {
- @Override
- public void onSizeCompatModeActivityChanged(int displayId,
- IBinder activityToken) {
- compatTokens.add(activityToken);
- }
- });
-
- // Resize the display so that the activity exercises size-compat mode.
- resizeDisplay(mTask.mDisplayContent, 1000, 2500);
-
- // Expect the exact token when the activity is in size compatibility mode.
- assertEquals(1, compatTokens.size());
- assertEquals(activity.appToken, compatTokens.get(0));
-
- compatTokens.clear();
- // Make the activity resizable again by restarting it
- activity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
- activity.mVisibleRequested = true;
- activity.restartProcessIfVisible();
- // The full lifecycle isn't hooked up so manually set state to resumed
- activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
- mTask.mDisplayContent.handleActivitySizeCompatModeIfNeeded(activity);
-
- // Expect null token when switching to non-size-compat mode activity.
- assertEquals(1, compatTokens.size());
- assertEquals(null, compatTokens.get(0));
- }
-
- /**
* Ensures that {@link TaskOrganizerController} can receive callback about the activity in size
* compatibility mode.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 8b604a3..3492d90 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -340,7 +340,7 @@
final WindowState w = new WindowState(service, session, iWindow, token, parent,
OP_NONE, attrs, VISIBLE, ownerId, userId,
- ownerCanAddInternalSystemWindow, false /* ownerCanUseBackgroundBlur */,
+ ownerCanAddInternalSystemWindow,
powerManagerWrapper);
// TODO: Probably better to make this call in the WindowState ctor to avoid errors with
// adding it to the token...
@@ -1213,8 +1213,7 @@
TestWindowState(WindowManagerService service, Session session, IWindow window,
WindowManager.LayoutParams attrs, WindowToken token) {
super(service, session, window, token, null, OP_NONE, attrs, 0, 0, 0,
- false /* ownerCanAddInternalSystemWindow */,
- false /* ownerCanUseBackgroundBlur */);
+ false /* ownerCanAddInternalSystemWindow */);
}
@Override
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index c1fb74d..be36f82 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1782,7 +1782,7 @@
}
@Override
- public void onPackageModified(String pkgName) {
+ public void onPackageModified(@NonNull String pkgName) {
// If the package modified is not in the current user, then don't bother making
// any changes as we are going to do any initialization needed when we switch users.
if (mCurUser != getChangingUserId()) {
diff --git a/tests/ApkVerityTest/Android.bp b/tests/ApkVerityTest/Android.bp
index 02c75ed..39dc9c2 100644
--- a/tests/ApkVerityTest/Android.bp
+++ b/tests/ApkVerityTest/Android.bp
@@ -16,6 +16,7 @@
name: "ApkVerityTest",
srcs: ["src/**/*.java"],
libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"],
+ static_libs: ["frameworks-base-hostutils"],
test_suites: ["general-tests", "vts"],
target_required: [
"block_device_writer_module",
diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
index 629b6c7..d0eb9be 100644
--- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
+++ b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
@@ -21,10 +21,10 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
import android.platform.test.annotations.RootPermissionTest;
+import com.android.fsverity.AddFsVerityCertRule;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil.CLog;
@@ -35,6 +35,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -85,40 +86,25 @@
private static final String DAMAGING_EXECUTABLE = "/data/local/tmp/block_device_writer";
private static final String CERT_PATH = "/data/local/tmp/ApkVerityTestCert.der";
- private static final String APK_VERITY_STANDARD_MODE = "2";
-
/** Only 4K page is supported by fs-verity currently. */
private static final int FSVERITY_PAGE_SIZE = 4096;
+ @Rule
+ public final AddFsVerityCertRule mAddFsVerityCertRule =
+ new AddFsVerityCertRule(this, CERT_PATH);
+
private ITestDevice mDevice;
- private String mKeyId;
@Before
public void setUp() throws DeviceNotAvailableException {
mDevice = getDevice();
- String apkVerityMode = mDevice.getProperty("ro.apk_verity.mode");
- assumeTrue(mDevice.getLaunchApiLevel() >= 30
- || APK_VERITY_STANDARD_MODE.equals(apkVerityMode));
-
- mKeyId = expectRemoteCommandToSucceed(
- "mini-keyctl padd asymmetric fsv_test .fs-verity < " + CERT_PATH).trim();
- if (!mKeyId.matches("^\\d+$")) {
- String keyId = mKeyId;
- mKeyId = null;
- fail("Key ID is not decimal: " + keyId);
- }
-
uninstallPackage(TARGET_PACKAGE);
}
@After
public void tearDown() throws DeviceNotAvailableException {
uninstallPackage(TARGET_PACKAGE);
-
- if (mKeyId != null) {
- expectRemoteCommandToSucceed("mini-keyctl unlink " + mKeyId + " .fs-verity");
- }
}
@Test
diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp
new file mode 100644
index 0000000..d809fe8
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/Android.bp
@@ -0,0 +1,30 @@
+// 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.
+
+java_test_host {
+ name: "UpdatableSystemFontTest",
+ srcs: ["src/**/*.java"],
+ libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"],
+ static_libs: ["frameworks-base-hostutils"],
+ test_suites: ["general-tests", "vts"],
+ data: [
+ ":NotoColorEmojiTtf",
+ ":UpdatableSystemFontTestCertDer",
+ ":UpdatableSystemFontTestNotoColorEmojiTtfFsvSig",
+ ":UpdatableSystemFontTestNotoColorEmojiV1Ttf",
+ ":UpdatableSystemFontTestNotoColorEmojiV1TtfFsvSig",
+ ":UpdatableSystemFontTestNotoColorEmojiV2Ttf",
+ ":UpdatableSystemFontTestNotoColorEmojiV2TtfFsvSig",
+ ],
+}
diff --git a/tests/UpdatableSystemFontTest/AndroidTest.xml b/tests/UpdatableSystemFontTest/AndroidTest.xml
new file mode 100644
index 0000000..efe5d70
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/AndroidTest.xml
@@ -0,0 +1,36 @@
+<?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.
+-->
+<configuration description="Updatable system font integration/regression test">
+ <option name="test-suite-tag" value="apct" />
+
+ <!-- This test requires root to side load fs-verity cert. -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="UpdatableSystemFontTestCert.der->/data/local/tmp/UpdatableSystemFontTestCert.der" />
+ <option name="push" value="NotoColorEmoji.ttf->/data/local/tmp/NotoColorEmoji.ttf" />
+ <option name="push" value="UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig" />
+ <option name="push" value="UpdatableSystemFontTestNotoColorEmojiV1.ttf->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV1.ttf" />
+ <option name="push" value="UpdatableSystemFontTestNotoColorEmojiV1.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV1.ttf.fsv_sig" />
+ <option name="push" value="UpdatableSystemFontTestNotoColorEmojiV2.ttf->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV2.ttf" />
+ <option name="push" value="UpdatableSystemFontTestNotoColorEmojiV2.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV2.ttf.fsv_sig" />
+ </target_preparer>
+
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="UpdatableSystemFontTest.jar" />
+ </test>
+</configuration>
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
new file mode 100644
index 0000000..6d161a5
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.updatablesystemfont;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.platform.test.annotations.RootPermissionTest;
+
+import com.android.fsverity.AddFsVerityCertRule;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Tests if fonts can be updated by 'cmd font'.
+ */
+@RootPermissionTest
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class UpdatableSystemFontTest extends BaseHostJUnit4Test {
+
+ private static final String CERT_PATH = "/data/local/tmp/UpdatableSystemFontTestCert.der";
+
+ private static final Pattern PATTERN_FONT = Pattern.compile("path = ([^, \n]*)");
+ private static final String NOTO_COLOR_EMOJI_TTF = "NotoColorEmoji.ttf";
+ private static final String TEST_NOTO_COLOR_EMOJI_V1_TTF =
+ "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV1.ttf";
+ private static final String TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG =
+ "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV1.ttf.fsv_sig";
+ private static final String TEST_NOTO_COLOR_EMOJI_V2_TTF =
+ "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV2.ttf";
+ private static final String TEST_NOTO_COLOR_EMOJI_V2_TTF_FSV_SIG =
+ "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV2.ttf.fsv_sig";
+ private static final String ORIGINAL_NOTO_COLOR_EMOJI_TTF =
+ "/data/local/tmp/NotoColorEmoji.ttf";
+ private static final String ORIGINAL_NOTO_COLOR_EMOJI_TTF_FSV_SIG =
+ "/data/local/tmp/UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig";
+
+ @Rule
+ public final AddFsVerityCertRule mAddFsverityCertRule =
+ new AddFsVerityCertRule(this, CERT_PATH);
+
+ @Before
+ public void setUp() throws Exception {
+ expectRemoteCommandToSucceed("cmd font clear");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ expectRemoteCommandToSucceed("cmd font clear");
+ }
+
+ @Test
+ public void updateFont() throws Exception {
+ expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
+ TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG));
+ String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF);
+ assertThat(fontPath).startsWith("/data/fonts/files/");
+ }
+
+ @Test
+ public void updateFont_twice() throws Exception {
+ expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
+ TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG));
+ String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF);
+ expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
+ TEST_NOTO_COLOR_EMOJI_V2_TTF, TEST_NOTO_COLOR_EMOJI_V2_TTF_FSV_SIG));
+ String fontPath2 = getFontPath(NOTO_COLOR_EMOJI_TTF);
+ assertThat(fontPath2).startsWith("/data/fonts/files/");
+ assertThat(fontPath2).isNotEqualTo(fontPath);
+ }
+
+ @Test
+ public void updatedFont_dataFileIsImmutableAndReadable() throws Exception {
+ expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
+ TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG));
+ String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF);
+ assertThat(fontPath).startsWith("/data");
+
+ expectRemoteCommandToFail("echo -n '' >> " + fontPath);
+ expectRemoteCommandToSucceed("cat " + fontPath + " > /dev/null");
+ }
+
+ @Test
+ public void updateFont_invalidCert() throws Exception {
+ expectRemoteCommandToFail(String.format("cmd font update %s %s",
+ TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V2_TTF_FSV_SIG));
+ }
+
+ @Test
+ public void updateFont_downgradeFromSystem() throws Exception {
+ expectRemoteCommandToFail(String.format("cmd font update %s %s",
+ ORIGINAL_NOTO_COLOR_EMOJI_TTF, ORIGINAL_NOTO_COLOR_EMOJI_TTF_FSV_SIG));
+ }
+
+ @Test
+ public void updateFont_downgradeFromData() throws Exception {
+ expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
+ TEST_NOTO_COLOR_EMOJI_V2_TTF, TEST_NOTO_COLOR_EMOJI_V2_TTF_FSV_SIG));
+ expectRemoteCommandToFail(String.format("cmd font update %s %s",
+ TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG));
+ }
+
+ private String getFontPath(String fontFileName) throws Exception {
+ // TODO: add a dedicated command for testing.
+ String lines = expectRemoteCommandToSucceed("cmd font dump");
+ for (String line : lines.split("\n")) {
+ Matcher m = PATTERN_FONT.matcher(line);
+ if (m.find() && m.group(1).endsWith(fontFileName)) {
+ return m.group(1);
+ }
+ }
+ CLog.e("Font not found: " + fontFileName);
+ return null;
+ }
+
+ private String expectRemoteCommandToSucceed(String cmd) throws Exception {
+ CommandResult result = getDevice().executeShellV2Command(cmd);
+ assertWithMessage("`" + cmd + "` failed: " + result.getStderr())
+ .that(result.getStatus())
+ .isEqualTo(CommandStatus.SUCCESS);
+ return result.getStdout();
+ }
+
+ private void expectRemoteCommandToFail(String cmd) throws Exception {
+ CommandResult result = getDevice().executeShellV2Command(cmd);
+ assertWithMessage("Unexpected success from `" + cmd + "`: " + result.getStderr())
+ .that(result.getStatus())
+ .isNotEqualTo(CommandStatus.SUCCESS);
+ }
+}
diff --git a/tests/UpdatableSystemFontTest/testdata/Android.bp b/tests/UpdatableSystemFontTest/testdata/Android.bp
new file mode 100644
index 0000000..1296699
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/Android.bp
@@ -0,0 +1,82 @@
+// 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.
+
+filegroup {
+ name: "UpdatableSystemFontTestKeyPem",
+ srcs: ["UpdatableSystemFontTestKey.pem"],
+}
+
+filegroup {
+ name: "UpdatableSystemFontTestCertPem",
+ srcs: ["UpdatableSystemFontTestCert.pem"],
+}
+
+filegroup {
+ name: "UpdatableSystemFontTestCertDer",
+ srcs: ["UpdatableSystemFontTestCert.der"],
+}
+
+genrule_defaults {
+ name: "updatable_system_font_increment_font_revision_default",
+ tools: ["update_font_metadata"],
+ cmd: "$(location update_font_metadata) " +
+ "--input=$(in) " +
+ "--output=$(out) " +
+ "--revision=+1",
+}
+
+genrule {
+ name: "UpdatableSystemFontTestNotoColorEmojiV1Ttf",
+ defaults: ["updatable_system_font_increment_font_revision_default"],
+ srcs: [":NotoColorEmojiTtf"],
+ out: ["UpdatableSystemFontTestNotoColorEmojiV1.ttf"],
+}
+
+genrule {
+ name: "UpdatableSystemFontTestNotoColorEmojiV2Ttf",
+ defaults: ["updatable_system_font_increment_font_revision_default"],
+ srcs: [":UpdatableSystemFontTestNotoColorEmojiV1Ttf"],
+ out: ["UpdatableSystemFontTestNotoColorEmojiV2.ttf"],
+}
+
+genrule_defaults {
+ name: "updatable_system_font_sig_gen_default",
+ tools: ["fsverity"],
+ tool_files: [":UpdatableSystemFontTestKeyPem", ":UpdatableSystemFontTestCertPem"],
+ cmd: "$(location fsverity) sign $(in) $(out) " +
+ "--key=$(location :UpdatableSystemFontTestKeyPem) " +
+ "--cert=$(location :UpdatableSystemFontTestCertPem) " +
+ "> /dev/null",
+}
+
+genrule {
+ name: "UpdatableSystemFontTestNotoColorEmojiTtfFsvSig",
+ defaults: ["updatable_system_font_sig_gen_default"],
+ srcs: [":NotoColorEmojiTtf"],
+ out: ["UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig"],
+}
+
+genrule {
+ name: "UpdatableSystemFontTestNotoColorEmojiV1TtfFsvSig",
+ defaults: ["updatable_system_font_sig_gen_default"],
+ srcs: [":UpdatableSystemFontTestNotoColorEmojiV1Ttf"],
+ out: ["UpdatableSystemFontTestNotoColorEmojiV1.ttf.fsv_sig"],
+}
+
+genrule {
+ name: "UpdatableSystemFontTestNotoColorEmojiV2TtfFsvSig",
+ defaults: ["updatable_system_font_sig_gen_default"],
+ srcs: [":UpdatableSystemFontTestNotoColorEmojiV2Ttf"],
+ out: ["UpdatableSystemFontTestNotoColorEmojiV2.ttf.fsv_sig"],
+}
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestCert.der b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestCert.der
new file mode 100644
index 0000000..f7aa15f
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestCert.der
Binary files differ
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestCert.pem b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestCert.pem
new file mode 100644
index 0000000..0cd1f66
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestCert.pem
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIFOTCCAyGgAwIBAgIUFaI1D5NtwkCVM3G4bFZ6sQSb598wDQYJKoZIhvcNAQEL
+BQAwLDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRAwDgYDVQQKDAdBbmRyb2lk
+MB4XDTIxMDIwMTA3MzAyNFoXDTIxMDMwMzA3MzAyNFowLDELMAkGA1UEBhMCVVMx
+CzAJBgNVBAgMAkNBMRAwDgYDVQQKDAdBbmRyb2lkMIICIjANBgkqhkiG9w0BAQEF
+AAOCAg8AMIICCgKCAgEA0U1zptc41E65ooeBPD33Mjgp6cYPydyj2Acq80Xy7lP7
+d6/2t6w7nNNl2x3n8dAhOl3de3IxTp6JI2SdoRb7obfcp+hWJoo/cxnHpr3q/u4R
+KED0rmWaOVHpGbajSTFZgN+cTbTKJbgtXm/H65x1QfO18ep/vj5fRiu1xPpJqDv/
+xuvuko2U3eC2+NayxzCWXVFrKPLx8GvzSQ3Utaug17vs7/5GqkRJgq3lk4DvmjNA
+vY8YA4RAkII1sSaceAWFEG6ztENLu2kjcxAI9qHxxBwQZit/NtFVlFGqSN3MEYjS
+M9Fz04RsUxF672QJpAgwCJDZ41rdB3hkHvOUK9PcepBsHdZq9cQ+E64+TX+jsJLu
+VouViKlYr6WYjvhfqZeRhwbj7CoEZ2DyEZKrl27fgWaidUT5LGEQLVxg90ymbimI
+6UwXRUwmRBQJBdRO4RGvngtqxRuamyjAKDDHx5YccXCX4FWLUypyQlz0asojvbJZ
+Og7DFa1qsRdGrGIRoQJ8pYnAjBJfSudr1l0mR7fZSfZc0W9ZmuROWx9Ip7aJWQnQ
+8JLtbNPuFLD2qbmg9Y1lcXJp1FvI9FcM8JsBqZNEANQwwsdTaa8gw+3W6J2SXKQP
+H+yZI/fJWWWRFADtqmpxtvXK9K+Cy1HQmg7D0IIxVPp8rrbz6TGrg+R3/N9FBWMC
+AwEAAaNTMFEwHQYDVR0OBBYEFDS48o2UAstoOyLMcgamHKrZdl3cMB8GA1UdIwQY
+MBaAFDS48o2UAstoOyLMcgamHKrZdl3cMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
+hvcNAQELBQADggIBAF7taBYAe20tWZu0pY9d4Z8il4LoJcRrKF4YiA02UizErgCF
+h4iECy6+pcu7DJUfvCh3dCWE7CDG+OnfUWTwEHVG9n8XI/ydetBUG76PZwTadI7B
+gzJ1y7/vWqJo5U6ki+sXNmq3hkgNsNZgza3LpdovkWJYeRdffM6m/bimzwYx9id8
+5mKw2PcbVZcb25r+0dCoLVJsqqCoRjdYUy/MKPutWG2bPzmaIv8KsKFN+mzlwhJH
+lpJ/LR+3NoaHrOCFG7CW/2Ihe501vmdQ2m/VKosyk0igw8WmTsY6xMbw2t77yKkD
+hnJr1NbhKeEV9gAB2BFX8nRWI7NTgp8fG78YLVz1UcbIHmYLgFoc3ezyma+CoR86
+ER20lKd4+TNnz4RtaPdZlBa0Ba3bsMtEneqlrHvcPrZ5tgGsQR9+cy3ZtTZ/LUQX
++Xuj/EoJXuuB3hkhg52zawN5n7WUe8efWHcv1jHqeIj0phcgbZ6u4fFBPsYjzDKe
+VuYHXglNOchmoBQwEaJI/TCiEgI8dcSJXSquLAXrtznVnxzT46ZMEt5LaW1/1NLx
+q//yoPdolCI0lpunh5jvIZJpUl5XMjxVSyaveQDNVqJkITWzWqIxAT5yTLtkCNlW
+c1XyzeHkpMItiJtBruExmnaTmNjlVKsXP8wQFOYbDGgXY5iHIMbgovptRyH/
+-----END CERTIFICATE-----
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestKey.pem b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestKey.pem
new file mode 100644
index 0000000..09bb104
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestKey.pem
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDRTXOm1zjUTrmi
+h4E8PfcyOCnpxg/J3KPYByrzRfLuU/t3r/a3rDuc02XbHefx0CE6Xd17cjFOnokj
+ZJ2hFvuht9yn6FYmij9zGcemver+7hEoQPSuZZo5UekZtqNJMVmA35xNtMoluC1e
+b8frnHVB87Xx6n++Pl9GK7XE+kmoO//G6+6SjZTd4Lb41rLHMJZdUWso8vHwa/NJ
+DdS1q6DXu+zv/kaqREmCreWTgO+aM0C9jxgDhECQgjWxJpx4BYUQbrO0Q0u7aSNz
+EAj2ofHEHBBmK3820VWUUapI3cwRiNIz0XPThGxTEXrvZAmkCDAIkNnjWt0HeGQe
+85Qr09x6kGwd1mr1xD4Trj5Nf6Owku5Wi5WIqVivpZiO+F+pl5GHBuPsKgRnYPIR
+kquXbt+BZqJ1RPksYRAtXGD3TKZuKYjpTBdFTCZEFAkF1E7hEa+eC2rFG5qbKMAo
+MMfHlhxxcJfgVYtTKnJCXPRqyiO9slk6DsMVrWqxF0asYhGhAnylicCMEl9K52vW
+XSZHt9lJ9lzRb1ma5E5bH0intolZCdDwku1s0+4UsPapuaD1jWVxcmnUW8j0Vwzw
+mwGpk0QA1DDCx1NpryDD7dbonZJcpA8f7Jkj98lZZZEUAO2qanG29cr0r4LLUdCa
+DsPQgjFU+nyutvPpMauD5Hf830UFYwIDAQABAoICAQCTVTcFCdl6MdSg4UwK0P/S
+fRCb/A0fJs67Agis6N9h/wI0NUyx7G6mLXU0si+U29KYGH0RKcgltJmKrYf8XoZR
+R3DvTTBfvs99QXd2G5hxTboMIPVcUi8nDE7PB+6XVkLP4hhP5uSpeqWNJZiQdTlh
+bKH2IgE8NQGyDpDMkPcKkvmw2GG/DiTtrwJ91fxRFRWzqN2LHMFMYWEHWtIR9Der
+xSC7q72om5s3fxvtIkUHwe5fwXvA9fbRAqezBR/9qL0LXTHowbpsuUz38SCuJD9g
+sfSlRxcsyly4pGf/FQpSiYKWcWlcSopKSzLDkyLqMc1GKlkGnu6aFJg95W63D1LS
+OaOXuYShHxLkqyhT8uQGRqDCu3E2ivb6fMxAPzJxxs3JrZvumNsqyxbp+HVF0idj
+NijMN/8Kb4KmNHG9I3SHG61tQFYDtxoMMNiHzq3fafBJnVcf6iThQdE5pGLN2OdF
+3rcSTeHI2HxhTrXtuiHmWXNk9aZ2TOhrssNZZjDkFL/KYh6G8guy+tn66YWy77VC
+id+6PBzYXTXsUauo4NWW1rLUfzT/y93IwVGpoXs7GHUUduZ6Q3PxsMTMF9IjBvqR
+JvfP84CUfGXoebVJmWyGhtW6N5ParvQxbonDitA0TPZcRyX3N8yqIGO/mb8MWA9M
+7s/xMZuksOpw5LSCoTAW6QKCAQEA9Z1WM/5XT+5HkwPBvS5aRz0SqMqPxAkZ0dNz
+O06zpXv6e2IPy40UzFWCIkyq3vWKQ5bqU1fnejUdmjvtnP+KhH6fxnQCgiunnrDq
+j1Sk2Y4gb1KyZY/C8IejOexM2qX7sfDTLI8XEvxJxVFCNmvYfnv4AX5QD8VOsjrg
+bvodLAgDSo2FVDP+mkpW7zAIoRV02l6QdZA+YcG940eqxB9sPR3/1KUHe9wUTTbJ
+FV5ahEPuXCyvRJkZ/rD5CPPZoQHfjKHxDlu7yfoatzCSYj10R+RInfqOFGVY4D/C
+2csXjymwTN4CFUnJcP410YhPFn5ekmc/E3xqPKgIDQhQ2+oivwKCAQEA2icN1iEo
+YuBJwB3pX3jrwk+1bpUXASueWhyAhSeNMrTrJ/lSgEAy9FBxJNnK1PjkABRnFhS+
+uxbC2hQdfAkNDS21PQOk6hOhebUVuBdfmKY1CL+P9y4Af0fjV1rgRGHihnZB5lKU
+1R/wFb8c4QgPwduiqDoZ5QP3dgxpZ4R3SCzuoyVelUSlgyWPoslg+yPddcnV8bTf
+BUlEOOyXgVudkSFlRpWZ+/ZAkLTj8rnrtKp8/+GtTQvJvPJHPfuSsDhI+EQVWqab
+HelMhxvIi9xOyN/OCc3Ex6JlEVa+X8EyNhQsV2sAKnld92hdEozW2uxRniIkxvL4
+CuBp/p3fWxEaXQKCAQEAw2j7RYCMnNZZ8Zhiko4HW3g2mT4XpYMMHMlbe4sBGJ8L
+yRBaurqzGmLJl1ph8+NsrpuqMMbWLn+F3sjhIjCZVxKbMbvopwHuaS4eYAya30/Z
+dFhaAL2g/dccQSBEgQzftFGC4YeydvNsCeW9hSjGZNNinGWPcwyqsNhw6Tpq7TYu
+0CjKNBTt8nlEsyYHJ4m3n2jvC+nIB+Spm+LP9Rt+9R0iBl+KFbwiFtCIqUyZPXQC
+dylCBJS+fsj0SXAg7J1d6ziIXcEUJfyrNqYZQLneAriYIcBPO+DqFfgEoVyYkNk9
+H9rd02wSLajCzsLhEWdW/KnSIEGzEDErvpqoIl8kZwKCAQEAjNO3T+sZyjKmCXqF
+xBcogsi4BAoEzsGcuOk7Yjn1Ia2/PI/r3VUUT7l6QOLD2JZPgWmqXovH0LjR0rw3
+iHHDViWSoS+wD1fa3tmyiqO0F7P7+ojHZDbzJTeAIE1PB3X1KP5AbnITGD5E25UD
+DJYKrgeeSmEvhDL6Vd+PT78ozZQL/Y/LLishebcOsXS0wYsWlMpV7XHoot34R5Mb
+/urond7kJRvASvJeHcxYdsHk0j1Y8kp6eIlKk0oICZBU0qOTH4m8C0gQTM/lkjay
+UO9IgM5RkOyfwowoGHhZ7zClvFlrgodVlRXCPkvGAYqfzLXPvnimKzSAQW07n53E
+qWIyFQKCAQAWRNG6hPCOzkNxMJ/RK7dwxZW6b4a1L3PMXTT/xrBKRIS1WNjbpkYO
+/FLIufOqJT6FQN2obM5uso3TI+R7MwH8DnTSnDDy0Hvs3CdHdtn2tapZOViF/UVv
+uCQa+/jMVKFCZ8k7pPFMIG6tB6WBA5MmJrW+8s0ouxLbRF5rZyzqOeyPdVBYYYDb
+68nGNA6GAtTQs9h7xV2tsQ1bXNP+6gqG3BgZYo+76xKITddT6s9aaC++LVBnPOdq
+LHV7gUvoBkVLjIp4L3Fb/DGMCcOVMCxmFlRBn+RBlV7slehvgq+Ywz2GHWLr+O/z
+V2NAtwvCfZE2Do/4f2mpHnamhS6AvrDe
+-----END PRIVATE KEY-----
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index e630da0..ffde68e 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -36,7 +36,7 @@
"libvndksupport",
"libziparchive",
"libz",
- "netd_aidl_interface-cpp",
+ "netd_aidl_interface-V5-cpp",
],
}
diff --git a/tests/utils/hostutils/src/com/android/fsverity/AddFsVerityCertRule.java b/tests/utils/hostutils/src/com/android/fsverity/AddFsVerityCertRule.java
new file mode 100644
index 0000000..5ab4dc6
--- /dev/null
+++ b/tests/utils/hostutils/src/com/android/fsverity/AddFsVerityCertRule.java
@@ -0,0 +1,77 @@
+/*
+ * 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.fsverity;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import org.junit.rules.ExternalResource;
+
+public final class AddFsVerityCertRule extends ExternalResource {
+
+ private static final String APK_VERITY_STANDARD_MODE = "2";
+
+ private final BaseHostJUnit4Test mHost;
+ private final String mCertPath;
+ private String mKeyId;
+
+ public AddFsVerityCertRule(BaseHostJUnit4Test host, String certPath) {
+ mHost = host;
+ mCertPath = certPath;
+ }
+
+ @Override
+ protected void before() throws Throwable {
+ ITestDevice device = mHost.getDevice();
+ String apkVerityMode = device.getProperty("ro.apk_verity.mode");
+ assumeTrue(device.getLaunchApiLevel() >= 30
+ || APK_VERITY_STANDARD_MODE.equals(apkVerityMode));
+
+ String keyId = executeCommand(
+ "mini-keyctl padd asymmetric fsv_test .fs-verity < " + mCertPath).trim();
+ assertThat(keyId).matches("^\\d+$");
+ mKeyId = keyId;
+ }
+
+ @Override
+ protected void after() {
+ if (mKeyId == null) return;
+ try {
+ executeCommand("mini-keyctl unlink " + mKeyId + " .fs-verity");
+ } catch (DeviceNotAvailableException e) {
+ LogUtil.CLog.e(e);
+ }
+ mKeyId = null;
+ }
+
+ private String executeCommand(String cmd) throws DeviceNotAvailableException {
+ CommandResult result = mHost.getDevice().executeShellV2Command(cmd);
+ assertWithMessage("`" + cmd + "` failed: " + result.getStderr())
+ .that(result.getStatus())
+ .isEqualTo(CommandStatus.SUCCESS);
+ return result.getStdout();
+ }
+}
diff --git a/tools/fonts/update_font_metadata.py b/tools/fonts/update_font_metadata.py
new file mode 100755
index 0000000..c07a98a
--- /dev/null
+++ b/tools/fonts/update_font_metadata.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+
+import argparse
+
+from fontTools import ttLib
+
+
+def update_font_revision(font, revisionSpec):
+ if revisionSpec.startswith('+'):
+ font['head'].fontRevision += float(revisionSpec[1:])
+ else:
+ font['head'].fontRevision = float(revisionSpec)
+
+
+def main():
+ args_parser = argparse.ArgumentParser(description='Update font file metadata')
+ args_parser.add_argument('--input', help='Input otf/ttf font file.')
+ args_parser.add_argument('--output', help='Output file for updated font file.')
+ args_parser.add_argument('--revision', help='Updated font revision. Use + to update revision based on the current revision')
+ args = args_parser.parse_args()
+
+ font = ttLib.TTFont(args.input)
+ update_font_revision(font, args.revision)
+ font.save(args.output)
+
+if __name__ == "__main__":
+ main()