Merge "Only show Pip actions when menu is FULL (i.e. shown)" 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/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index 223f3b0..6ab1051 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -791,15 +791,10 @@
mNumUnspecialized = mConfigMaxTotal;
mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_TOP);
mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_BG);
- mNumUnspecialized -= mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP);
- mNumUnspecialized -= mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG);
- calculateUnspecializedRemaining();
- }
-
- private void calculateUnspecializedRemaining() {
- mNumUnspecializedRemaining = mNumUnspecialized;
+ mNumUnspecializedRemaining = mConfigMaxTotal;
for (int i = mNumRunningJobs.size() - 1; i >= 0; --i) {
- mNumUnspecializedRemaining -= mNumRunningJobs.valueAt(i);
+ mNumUnspecializedRemaining -= Math.max(mNumRunningJobs.valueAt(i),
+ mConfigNumReservedSlots.get(mNumRunningJobs.keyAt(i)));
}
}
@@ -882,24 +877,42 @@
mNumUnspecialized = mConfigMaxTotal;
final int numTop = mNumRunningJobs.get(WORK_TYPE_TOP)
+ mNumPendingJobs.get(WORK_TYPE_TOP);
- final int resTop = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_TOP), numTop);
+ int resTop = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_TOP), numTop);
mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop);
mNumUnspecialized -= resTop;
final int numBg = mNumRunningJobs.get(WORK_TYPE_BG) + mNumPendingJobs.get(WORK_TYPE_BG);
- final int resBg = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_BG), numBg);
+ int resBg = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_BG), numBg);
mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg);
mNumUnspecialized -= resBg;
- calculateUnspecializedRemaining();
+
+ mNumUnspecializedRemaining = mNumUnspecialized;
+ // Account for already running jobs after we've assigned the minimum number of slots.
+ int unspecializedAssigned;
+ int extraRunning = (mNumRunningJobs.get(WORK_TYPE_TOP) - resTop);
+ if (extraRunning > 0) {
+ unspecializedAssigned = Math.max(0,
+ Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP) - resTop,
+ extraRunning));
+ resTop += unspecializedAssigned;
+ mNumUnspecializedRemaining -= extraRunning;
+ }
+ extraRunning = (mNumRunningJobs.get(WORK_TYPE_BG) - resBg);
+ if (extraRunning > 0) {
+ unspecializedAssigned = Math.max(0,
+ Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG) - resBg, extraRunning));
+ resBg += unspecializedAssigned;
+ mNumUnspecializedRemaining -= extraRunning;
+ }
// Assign remaining unspecialized based on ranking.
- int unspecializedAssigned = Math.max(0,
- Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP),
- Math.min(mNumUnspecializedRemaining, numTop - resTop)));
+ unspecializedAssigned = Math.max(0,
+ Math.min(mNumUnspecializedRemaining,
+ Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP), numTop) - resTop));
mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop + unspecializedAssigned);
mNumUnspecializedRemaining -= unspecializedAssigned;
unspecializedAssigned = Math.max(0,
- Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG),
- Math.min(mNumUnspecializedRemaining, numBg - resBg)));
+ Math.min(mNumUnspecializedRemaining,
+ Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG), numBg) - resBg));
mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg + unspecializedAssigned);
mNumUnspecializedRemaining -= unspecializedAssigned;
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 3efb789..62a3c38 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -157,7 +157,7 @@
field public static final String SET_WALLPAPER = "android.permission.SET_WALLPAPER";
field public static final String SET_WALLPAPER_HINTS = "android.permission.SET_WALLPAPER_HINTS";
field public static final String SIGNAL_PERSISTENT_PROCESSES = "android.permission.SIGNAL_PERSISTENT_PROCESSES";
- field public static final String SMS_FINANCIAL_TRANSACTIONS = "android.permission.SMS_FINANCIAL_TRANSACTIONS";
+ field @Deprecated public static final String SMS_FINANCIAL_TRANSACTIONS = "android.permission.SMS_FINANCIAL_TRANSACTIONS";
field public static final String START_VIEW_PERMISSION_USAGE = "android.permission.START_VIEW_PERMISSION_USAGE";
field public static final String STATUS_BAR = "android.permission.STATUS_BAR";
field public static final String SYSTEM_ALERT_WINDOW = "android.permission.SYSTEM_ALERT_WINDOW";
@@ -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
@@ -12355,6 +12355,8 @@
field public static final String FEATURE_INPUT_METHODS = "android.software.input_methods";
field public static final String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
field public static final String FEATURE_IRIS = "android.hardware.biometrics.iris";
+ field public static final String FEATURE_KEYSTORE_LIMITED_USE_KEY = "android.hardware.keystore.limited_use_key";
+ field public static final String FEATURE_KEYSTORE_SINGLE_USE_KEY = "android.hardware.keystore.single_use_key";
field public static final String FEATURE_LEANBACK = "android.software.leanback";
field public static final String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
field public static final String FEATURE_LIVE_TV = "android.software.live_tv";
@@ -21108,7 +21110,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);
@@ -36865,6 +36869,7 @@
method @Nullable public java.util.Date getKeyValidityForOriginationEnd();
method @Nullable public java.util.Date getKeyValidityStart();
method @NonNull public String getKeystoreAlias();
+ method public int getMaxUsageCount();
method public int getPurposes();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
@@ -36901,6 +36906,7 @@
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForConsumptionEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForOriginationEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityStart(java.util.Date);
+ method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMaxUsageCount(int);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean);
@@ -36923,6 +36929,7 @@
method public String getKeystoreAlias();
method public int getOrigin();
method public int getPurposes();
+ method public int getRemainingUsageCount();
method public int getSecurityLevel();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
@@ -36992,6 +36999,7 @@
field public static final int SECURITY_LEVEL_UNKNOWN_SECURE = -1; // 0xffffffff
field public static final String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1";
field public static final String SIGNATURE_PADDING_RSA_PSS = "PSS";
+ field public static final int UNRESTRICTED_USAGE_COUNT = -1; // 0xffffffff
}
public final class KeyProtection implements java.security.KeyStore.ProtectionParameter {
@@ -37001,6 +37009,7 @@
method @Nullable public java.util.Date getKeyValidityForConsumptionEnd();
method @Nullable public java.util.Date getKeyValidityForOriginationEnd();
method @Nullable public java.util.Date getKeyValidityStart();
+ method public int getMaxUsageCount();
method public int getPurposes();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
@@ -37027,6 +37036,7 @@
method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityForConsumptionEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityForOriginationEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityStart(java.util.Date);
+ method @NonNull public android.security.keystore.KeyProtection.Builder setMaxUsageCount(int);
method @NonNull public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean);
method @NonNull public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...);
method @NonNull public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean);
@@ -46138,6 +46148,7 @@
method @Deprecated public void getRectSize(android.graphics.Rect);
method public float getRefreshRate();
method public int getRotation();
+ method @Nullable public android.view.RoundedCorner getRoundedCorner(int);
method @Deprecated public void getSize(android.graphics.Point);
method public int getState();
method public android.view.Display.Mode[] getSupportedModes();
@@ -47392,6 +47403,20 @@
field public static final int TYPE_ZOOM_OUT = 1019; // 0x3fb
}
+ public final class RoundedCorner implements android.os.Parcelable {
+ ctor public RoundedCorner(int, int, int, int);
+ method public int describeContents();
+ method @NonNull public android.graphics.Point getCenter();
+ method public int getPosition();
+ method public int getRadius();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.RoundedCorner> CREATOR;
+ field public static final int POSITION_BOTTOM_LEFT = 3; // 0x3
+ field public static final int POSITION_BOTTOM_RIGHT = 2; // 0x2
+ field public static final int POSITION_TOP_LEFT = 0; // 0x0
+ field public static final int POSITION_TOP_RIGHT = 1; // 0x1
+ }
+
public class ScaleGestureDetector {
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener);
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener, android.os.Handler);
@@ -49401,6 +49426,7 @@
method @NonNull public android.graphics.Insets getInsets(int);
method @NonNull public android.graphics.Insets getInsetsIgnoringVisibility(int);
method @Deprecated @NonNull public android.graphics.Insets getMandatorySystemGestureInsets();
+ method @Nullable public android.view.RoundedCorner getRoundedCorner(int);
method @Deprecated public int getStableInsetBottom();
method @Deprecated public int getStableInsetLeft();
method @Deprecated public int getStableInsetRight();
@@ -49434,6 +49460,7 @@
method @NonNull public android.view.WindowInsets.Builder setInsets(int, @NonNull android.graphics.Insets);
method @NonNull public android.view.WindowInsets.Builder setInsetsIgnoringVisibility(int, @NonNull android.graphics.Insets) throws java.lang.IllegalArgumentException;
method @Deprecated @NonNull public android.view.WindowInsets.Builder setMandatorySystemGestureInsets(@NonNull android.graphics.Insets);
+ method @NonNull public android.view.WindowInsets.Builder setRoundedCorner(int, @Nullable android.view.RoundedCorner);
method @Deprecated @NonNull public android.view.WindowInsets.Builder setStableInsets(@NonNull android.graphics.Insets);
method @Deprecated @NonNull public android.view.WindowInsets.Builder setSystemGestureInsets(@NonNull android.graphics.Insets);
method @Deprecated @NonNull public android.view.WindowInsets.Builder setSystemWindowInsets(@NonNull android.graphics.Insets);
@@ -49586,6 +49613,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
@@ -54489,19 +54517,26 @@
method public void setByte(@IdRes int, String, byte);
method public void setChar(@IdRes int, String, char);
method public void setCharSequence(@IdRes int, String, CharSequence);
+ method public void setCharSequence(@IdRes int, @NonNull String, @StringRes int);
method public void setChronometer(@IdRes int, long, String, boolean);
method public void setChronometerCountDown(@IdRes int, boolean);
+ method public void setColor(@IdRes int, @NonNull String, @ColorRes int);
+ method public void setColorStateList(@IdRes int, @NonNull String, @ColorRes int);
method public void setContentDescription(@IdRes int, CharSequence);
method public void setDisplayedChild(@IdRes int, int);
method public void setDouble(@IdRes int, String, double);
method public void setEmptyView(@IdRes int, @IdRes int);
method public void setFloat(@IdRes int, String, float);
+ method public void setFloatDimen(@IdRes int, @NonNull String, @DimenRes int);
+ method public void setFloatDimen(@IdRes int, @NonNull String, float, int);
method public void setIcon(@IdRes int, String, android.graphics.drawable.Icon);
method public void setImageViewBitmap(@IdRes int, android.graphics.Bitmap);
method public void setImageViewIcon(@IdRes int, android.graphics.drawable.Icon);
method public void setImageViewResource(@IdRes int, @DrawableRes int);
method public void setImageViewUri(@IdRes int, android.net.Uri);
method public void setInt(@IdRes int, String, int);
+ method public void setIntDimen(@IdRes int, @NonNull String, @DimenRes int);
+ method public void setIntDimen(@IdRes int, @NonNull String, float, int);
method public void setIntent(@IdRes int, String, android.content.Intent);
method public void setLabelFor(@IdRes int, @IdRes int);
method public void setLightBackgroundLayoutId(@LayoutRes int);
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 c8d5a6b..2c2443f 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 {
@@ -4811,6 +4796,7 @@
public static class AudioAttributes.Builder {
method public android.media.AudioAttributes.Builder addBundle(@NonNull android.os.Bundle);
method public android.media.AudioAttributes.Builder setCapturePreset(int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD) public android.media.AudioAttributes.Builder setHotwordMode();
method public android.media.AudioAttributes.Builder setInternalCapturePreset(int);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioAttributes.Builder setSystemUsage(int);
}
@@ -14101,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 {
@@ -14374,6 +14358,7 @@
method public String getDataDirectorySuffix();
method public String getErrorString(android.content.Context, int);
method public int getPackageId(android.content.res.Resources, String);
+ method @NonNull public long[] getTimestamps();
method @Deprecated public void invokeDrawGlFunctor(android.view.View, long, boolean);
method public boolean isMultiProcessEnabled();
method public boolean isTraceTagEnabled();
@@ -14389,6 +14374,12 @@
method public static android.content.pm.PackageInfo getLoadedPackageInfo();
method public static int loadWebViewNativeLibraryFromPackage(String, ClassLoader);
method public static void prepareWebViewInZygote();
+ field public static final int ADD_ASSETS_END = 4; // 0x4
+ field public static final int ADD_ASSETS_START = 3; // 0x3
+ field public static final int CREATE_CONTEXT_END = 2; // 0x2
+ field public static final int CREATE_CONTEXT_START = 1; // 0x1
+ field public static final int GET_CLASS_LOADER_END = 6; // 0x6
+ field public static final int GET_CLASS_LOADER_START = 5; // 0x5
field public static final int LIBLOAD_ADDRESS_SPACE_NOT_RESERVED = 2; // 0x2
field public static final int LIBLOAD_FAILED_JNI_CALL = 7; // 0x7
field public static final int LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES = 4; // 0x4
@@ -14399,6 +14390,11 @@
field public static final int LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN = 8; // 0x8
field public static final int LIBLOAD_SUCCESS = 0; // 0x0
field public static final int LIBLOAD_WRONG_PACKAGE_NAME = 1; // 0x1
+ field public static final int NATIVE_LOAD_END = 8; // 0x8
+ field public static final int NATIVE_LOAD_START = 7; // 0x7
+ field public static final int PROVIDER_CLASS_FOR_NAME_END = 10; // 0xa
+ field public static final int PROVIDER_CLASS_FOR_NAME_START = 9; // 0x9
+ field public static final int WEBVIEW_LOAD_START = 0; // 0x0
}
public interface WebViewFactoryProvider {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index b0ee3f0..e0391ee 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -401,10 +401,10 @@
field public static final int CODE_MANAGED_USERS_NOT_SUPPORTED = 9; // 0x9
field public static final int CODE_NONSYSTEM_USER_EXISTS = 5; // 0x5
field public static final int CODE_NOT_SYSTEM_USER = 7; // 0x7
- field public static final int CODE_NOT_SYSTEM_USER_SPLIT = 12; // 0xc
+ field @Deprecated public static final int CODE_NOT_SYSTEM_USER_SPLIT = 12; // 0xc
field public static final int CODE_OK = 0; // 0x0
field public static final int CODE_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS = 15; // 0xf
- field public static final int CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14; // 0xe
+ field @Deprecated public static final int CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14; // 0xe
field public static final int CODE_SYSTEM_USER = 10; // 0xa
field public static final int CODE_USER_HAS_PROFILE_OWNER = 2; // 0x2
field public static final int CODE_USER_NOT_RUNNING = 3; // 0x3
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index bdd541a..4728f11 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5090,6 +5090,13 @@
mTaskDescription.setBackgroundColor(colorBackground);
}
+ int colorBackgroundFloating = a.getColor(
+ com.android.internal.R.styleable.ActivityTaskDescription_colorBackgroundFloating,
+ 0);
+ if (colorBackgroundFloating != 0 && Color.alpha(colorBackgroundFloating) == 0xFF) {
+ mTaskDescription.setBackgroundColorFloating(colorBackgroundFloating);
+ }
+
final int statusBarColor = a.getColor(
com.android.internal.R.styleable.ActivityTaskDescription_statusBarColor, 0);
if (statusBarColor != 0) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 520959c..43d0269 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1072,13 +1072,15 @@
private static final String ATTR_TASKDESCRIPTIONCOLOR_PRIMARY =
ATTR_TASKDESCRIPTION_PREFIX + "color";
private static final String ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND =
- ATTR_TASKDESCRIPTION_PREFIX + "colorBackground";
+ ATTR_TASKDESCRIPTION_PREFIX + "color_background";
private static final String ATTR_TASKDESCRIPTIONICON_FILENAME =
ATTR_TASKDESCRIPTION_PREFIX + "icon_filename";
private static final String ATTR_TASKDESCRIPTIONICON_RESOURCE =
ATTR_TASKDESCRIPTION_PREFIX + "icon_resource";
private static final String ATTR_TASKDESCRIPTIONICON_RESOURCE_PACKAGE =
ATTR_TASKDESCRIPTION_PREFIX + "icon_package";
+ private static final String ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND_FLOATING =
+ ATTR_TASKDESCRIPTION_PREFIX + "color_background_floating";
private String mLabel;
@Nullable
@@ -1086,6 +1088,7 @@
private String mIconFilename;
private int mColorPrimary;
private int mColorBackground;
+ private int mColorBackgroundFloating;
private int mStatusBarColor;
private int mNavigationBarColor;
private boolean mEnsureStatusBarContrastWhenTransparent;
@@ -1106,7 +1109,7 @@
*/
public TaskDescription(String label, @DrawableRes int iconRes, int colorPrimary) {
this(label, Icon.createWithResource(ActivityThread.currentPackageName(), iconRes),
- colorPrimary, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1);
+ colorPrimary, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) {
throw new RuntimeException("A TaskDescription's primary color should be opaque");
}
@@ -1121,7 +1124,7 @@
*/
public TaskDescription(String label, @DrawableRes int iconRes) {
this(label, Icon.createWithResource(ActivityThread.currentPackageName(), iconRes),
- 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1);
+ 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
}
/**
@@ -1130,14 +1133,14 @@
* @param label A label and description of the current state of this activity.
*/
public TaskDescription(String label) {
- this(label, null, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1);
+ this(label, null, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
}
/**
* Creates an empty TaskDescription.
*/
public TaskDescription() {
- this(null, null, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1);
+ this(null, null, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
}
/**
@@ -1152,7 +1155,7 @@
@Deprecated
public TaskDescription(String label, Bitmap icon, int colorPrimary) {
this(label, icon != null ? Icon.createWithBitmap(icon) : null, colorPrimary, 0, 0, 0,
- false, false, RESIZE_MODE_RESIZEABLE, -1, -1);
+ false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) {
throw new RuntimeException("A TaskDescription's primary color should be opaque");
}
@@ -1168,7 +1171,7 @@
@Deprecated
public TaskDescription(String label, Bitmap icon) {
this(label, icon != null ? Icon.createWithBitmap(icon) : null, 0, 0, 0, 0, false, false,
- RESIZE_MODE_RESIZEABLE, -1, -1);
+ RESIZE_MODE_RESIZEABLE, -1, -1, 0);
}
/** @hide */
@@ -1177,7 +1180,7 @@
int statusBarColor, int navigationBarColor,
boolean ensureStatusBarContrastWhenTransparent,
boolean ensureNavigationBarContrastWhenTransparent, int resizeMode, int minWidth,
- int minHeight) {
+ int minHeight, int colorBackgroundFloating) {
mLabel = label;
mIcon = icon;
mColorPrimary = colorPrimary;
@@ -1190,6 +1193,7 @@
mResizeMode = resizeMode;
mMinWidth = minWidth;
mMinHeight = minHeight;
+ mColorBackgroundFloating = colorBackgroundFloating;
}
/**
@@ -1217,6 +1221,7 @@
mResizeMode = other.mResizeMode;
mMinWidth = other.mMinWidth;
mMinHeight = other.mMinHeight;
+ mColorBackgroundFloating = other.mColorBackgroundFloating;
}
/**
@@ -1253,6 +1258,9 @@
if (other.mMinHeight != -1) {
mMinHeight = other.mMinHeight;
}
+ if (other.mColorBackgroundFloating != 0) {
+ mColorBackgroundFloating = other.mColorBackgroundFloating;
+ }
}
private TaskDescription(Parcel source) {
@@ -1292,6 +1300,19 @@
}
/**
+ * Sets the background color floating for this task description.
+ * @hide
+ */
+ public void setBackgroundColorFloating(int backgroundColor) {
+ // Ensure that the given color is valid
+ if ((backgroundColor != 0) && (Color.alpha(backgroundColor) != 255)) {
+ throw new RuntimeException(
+ "A TaskDescription's background color floating should be opaque");
+ }
+ mColorBackgroundFloating = backgroundColor;
+ }
+
+ /**
* @hide
*/
public void setStatusBarColor(int statusBarColor) {
@@ -1461,6 +1482,14 @@
}
/**
+ * @return The background color floating.
+ * @hide
+ */
+ public int getBackgroundColorFloating() {
+ return mColorBackgroundFloating;
+ }
+
+ /**
* @hide
*/
public int getStatusBarColor() {
@@ -1537,6 +1566,10 @@
if (mColorBackground != 0) {
out.attributeIntHex(null, ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND, mColorBackground);
}
+ if (mColorBackgroundFloating != 0) {
+ out.attributeIntHex(null, ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND_FLOATING,
+ mColorBackgroundFloating);
+ }
if (mIconFilename != null) {
out.attribute(null, ATTR_TASKDESCRIPTIONICON_FILENAME, mIconFilename);
}
@@ -1563,6 +1596,11 @@
if (colorBackground != 0) {
setBackgroundColor(colorBackground);
}
+ final int colorBackgroundFloating = in.getAttributeIntHex(null,
+ ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND_FLOATING, 0);
+ if (colorBackgroundFloating != 0) {
+ setBackgroundColorFloating(colorBackgroundFloating);
+ }
final String iconFilename = in.getAttributeValue(null,
ATTR_TASKDESCRIPTIONICON_FILENAME);
if (iconFilename != null) {
@@ -1615,6 +1653,7 @@
dest.writeInt(1);
dest.writeString(mIconFilename);
}
+ dest.writeInt(mColorBackgroundFloating);
}
public void readFromParcel(Parcel source) {
@@ -1632,6 +1671,7 @@
mMinWidth = source.readInt();
mMinHeight = source.readInt();
mIconFilename = source.readInt() > 0 ? source.readString() : null;
+ mColorBackgroundFloating = source.readInt();
}
public static final @android.annotation.NonNull Creator<TaskDescription> CREATOR
@@ -1655,7 +1695,8 @@
+ (mEnsureNavigationBarContrastWhenTransparent
? " (contrast when transparent)" : "")
+ " resizeMode: " + ActivityInfo.resizeModeToString(mResizeMode)
- + " minWidth: " + mMinWidth + " minHeight: " + mMinHeight;
+ + " minWidth: " + mMinWidth + " minHeight: " + mMinHeight
+ + " colorBackgrounFloating: " + mColorBackgroundFloating;
}
@Override
@@ -1678,7 +1719,8 @@
== other.mEnsureNavigationBarContrastWhenTransparent
&& mResizeMode == other.mResizeMode
&& mMinWidth == other.mMinWidth
- && mMinHeight == other.mMinHeight;
+ && mMinHeight == other.mMinHeight
+ && mColorBackgroundFloating == other.mColorBackgroundFloating;
}
/** @hide */
@@ -1826,6 +1868,8 @@
pw.print(ActivityInfo.resizeModeToString(td.getResizeMode()));
pw.print(" minWidth="); pw.print(td.getMinWidth());
pw.print(" minHeight="); pw.print(td.getMinHeight());
+ pw.print(" colorBackgroundFloating=#");
+ pw.print(Integer.toHexString(td.getBackgroundColorFloating()));
pw.println(" }");
}
}
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/app/admin/DevicePolicyCache.java b/core/java/android/app/admin/DevicePolicyCache.java
index 15ff531..8b0c706 100644
--- a/core/java/android/app/admin/DevicePolicyCache.java
+++ b/core/java/android/app/admin/DevicePolicyCache.java
@@ -51,6 +51,12 @@
public abstract int getPasswordQuality(@UserIdInt int userHandle);
/**
+ * Caches {@link DevicePolicyManager#getPermissionPolicy(android.content.ComponentName)} of
+ * the given user.
+ */
+ public abstract int getPermissionPolicy(@UserIdInt int userHandle);
+
+ /**
* Empty implementation.
*/
private static class EmptyDevicePolicyCache extends DevicePolicyCache {
@@ -66,5 +72,10 @@
public int getPasswordQuality(int userHandle) {
return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
}
+
+ @Override
+ public int getPermissionPolicy(int userHandle) {
+ return DevicePolicyManager.PERMISSION_POLICY_PROMPT;
+ }
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ada703b..e84d4a5 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -453,59 +453,6 @@
"android.app.action.PROVISION_FINANCED_DEVICE";
/**
- * Activity action: Starts the provisioning flow which sets up a managed device.
- * Must be started with {@link android.app.Activity#startActivityForResult(Intent, int)}.
- *
- * <p>NOTE: This is only supported on split system user devices, and puts the device into a
- * management state that is distinct from that reached by
- * {@link #ACTION_PROVISION_MANAGED_DEVICE} - specifically the device owner runs on the system
- * user, and only has control over device-wide policies, not individual users and their data.
- * The primary benefit is that multiple non-system users are supported when provisioning using
- * this form of device management.
- *
- * <p>During device owner provisioning a device admin app is set as the owner of the device.
- * A device owner has full control over the device. The device owner can not be modified by the
- * user.
- *
- * <p>A typical use case would be a device that is owned by a company, but used by either an
- * employee or client.
- *
- * <p>An intent with this action can be sent only on an unprovisioned device.
- * It is possible to check if provisioning is allowed or not by querying the method
- * {@link #isProvisioningAllowed(String)}.
- *
- * <p>The intent contains the following extras:
- * <ul>
- * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}</li>
- * <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_LOGO_URI}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_MAIN_COLOR}, optional</li>
- * </ul>
- *
- * <p>When device owner provisioning has completed, an intent of the type
- * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE} is broadcast to the
- * device owner.
- *
- * <p>From version {@link android.os.Build.VERSION_CODES#O}, when device owner provisioning has
- * completed, along with the above broadcast, activity intent
- * {@link #ACTION_PROVISIONING_SUCCESSFUL} will also be sent to the device owner.
- *
- * <p>If provisioning fails, the device is factory reset.
- *
- * <p>A result code of {@link android.app.Activity#RESULT_OK} implies that the synchronous part
- * of the provisioning flow was successful, although this doesn't guarantee the full flow will
- * succeed. Conversely a result code of {@link android.app.Activity#RESULT_CANCELED} implies
- * that the user backed-out of provisioning, or some precondition for provisioning wasn't met.
- *
- * @hide
- */
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE
- = "android.app.action.PROVISION_MANAGED_SHAREABLE_DEVICE";
-
- /**
* Activity action: Finalizes management provisioning, should be used after user-setup
* has been completed and {@link #getUserProvisioningState()} returns one of:
* <ul>
@@ -1990,8 +1937,8 @@
* Result code for {@link #checkProvisioningPreCondition}.
*
* <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
- * {@link #ACTION_PROVISION_MANAGED_PROFILE}, {@link #ACTION_PROVISION_MANAGED_USER} and
- * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} when provisioning is allowed.
+ * {@link #ACTION_PROVISION_MANAGED_PROFILE} and {@link #ACTION_PROVISION_MANAGED_USER}
+ * when provisioning is allowed.
*
* @hide
*/
@@ -2001,9 +1948,8 @@
/**
* Result code for {@link #checkProvisioningPreCondition}.
*
- * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} and
- * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} when the device already has a device
- * owner.
+ * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} when the device already has a
+ * device owner.
*
* @hide
*/
@@ -2013,9 +1959,8 @@
/**
* Result code for {@link #checkProvisioningPreCondition}.
*
- * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
- * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} when the user has a profile owner and for
- * {@link #ACTION_PROVISION_MANAGED_PROFILE} when the profile owner is already set.
+ * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} when the user has a profile owner
+ * and for {@link #ACTION_PROVISION_MANAGED_PROFILE} when the profile owner is already set.
*
* @hide
*/
@@ -2025,8 +1970,7 @@
/**
* Result code for {@link #checkProvisioningPreCondition}.
*
- * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} and
- * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} when the user isn't running.
+ * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} when the user isn't running.
*
* @hide
*/
@@ -2036,9 +1980,8 @@
/**
* Result code for {@link #checkProvisioningPreCondition}.
*
- * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
- * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} if the device has already been setup and
- * for {@link #ACTION_PROVISION_MANAGED_USER} if the user has already been setup.
+ * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} if the device has already been
+ * setup and for {@link #ACTION_PROVISION_MANAGED_USER} if the user has already been setup.
*
* @hide
*/
@@ -2064,8 +2007,7 @@
/**
* Result code for {@link #checkProvisioningPreCondition}.
*
- * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} and
- * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} if the user is not a system user.
+ * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} if the user is not a system user.
*
* @hide
*/
@@ -2075,9 +2017,8 @@
/**
* Result code for {@link #checkProvisioningPreCondition}.
*
- * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
- * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} and {@link #ACTION_PROVISION_MANAGED_USER}
- * when the device is a watch and is already paired.
+ * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} and
+ * {@link #ACTION_PROVISION_MANAGED_USER} when the device is a watch and is already paired.
*
* @hide
*/
@@ -2121,14 +2062,11 @@
/**
* TODO (b/137101239): clean up split system user codes
- * Result code for {@link #checkProvisioningPreCondition}.
- *
- * <p>Returned for {@link #ACTION_PROVISION_MANAGED_USER} and
- * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} on devices not running with split system
- * user.
*
* @hide
- */
+ * @deprecated not used anymore but can't be removed since it's a @TestApi.
+ **/
+ @Deprecated
@TestApi
public static final int CODE_NOT_SYSTEM_USER_SPLIT = 12;
@@ -2136,8 +2074,7 @@
* Result code for {@link #checkProvisioningPreCondition}.
*
* <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
- * {@link #ACTION_PROVISION_MANAGED_PROFILE}, {@link #ACTION_PROVISION_MANAGED_USER} and
- * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} on devices which do not support device
+ * {@link #ACTION_PROVISION_MANAGED_PROFILE} on devices which do not support device
* admins.
*
* @hide
@@ -2149,11 +2086,10 @@
* TODO (b/137101239): clean up split system user codes
* Result code for {@link #checkProvisioningPreCondition}.
*
- * <p>Returned for {@link #ACTION_PROVISION_MANAGED_PROFILE} when the device the user is a
- * system user on a split system user device.
- *
* @hide
+ * @deprecated not used anymore but can't be removed since it's a @TestApi.
*/
+ @Deprecated
@TestApi
public static final int CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14;
@@ -7147,17 +7083,9 @@
}
/**
+ * TODO (b/137101239): remove this method in follow-up CL
+ * since it's only used for split system user.
* Called by a device owner to set whether all users created on the device should be ephemeral.
- * <p>
- * The system user is exempt from this policy - it is never ephemeral.
- * <p>
- * The calling device admin must be the device owner. If it is not, a security exception will be
- * thrown.
- *
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @param forceEphemeralUsers If true, all the existing users will be deleted and all
- * subsequently created users will be ephemeral.
- * @throws SecurityException if {@code admin} is not a device owner.
* @hide
*/
public void setForceEphemeralUsers(
@@ -7173,6 +7101,8 @@
}
/**
+ * TODO (b/137101239): remove this method in follow-up CL
+ * since it's only used for split system user.
* @return true if all users are created ephemeral.
* @throws SecurityException if {@code admin} is not a device owner.
* @hide
@@ -10731,9 +10661,7 @@
* profile or user, setting the given package as owner.
*
* @param action One of {@link #ACTION_PROVISION_MANAGED_DEVICE},
- * {@link #ACTION_PROVISION_MANAGED_PROFILE},
- * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE},
- * {@link #ACTION_PROVISION_MANAGED_USER}
+ * {@link #ACTION_PROVISION_MANAGED_PROFILE}
* @param packageName The package of the component that would be set as device, user, or profile
* owner.
* @return A {@link ProvisioningPreCondition} value indicating whether provisioning is allowed.
diff --git a/core/java/android/app/smartspace/OWNERS b/core/java/android/app/smartspace/OWNERS
new file mode 100644
index 0000000..19ef9d7
--- /dev/null
+++ b/core/java/android/app/smartspace/OWNERS
@@ -0,0 +1,2 @@
+srazdan@google.com
+alexmang@google.com
\ No newline at end of file
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 1ddfe0d..1d5dc1d 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -28,7 +28,6 @@
import android.net.ConnectivityManager;
import android.net.DataUsageRequest;
import android.net.INetworkStatsService;
-import android.net.NetworkIdentity;
import android.net.NetworkStack;
import android.net.NetworkTemplate;
import android.net.netstats.provider.INetworkStatsProviderCallback;
@@ -47,6 +46,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.NetworkIdentityUtils;
import java.util.Objects;
@@ -628,7 +628,7 @@
default:
throw new IllegalArgumentException("Cannot create template for network type "
+ networkType + ", subscriberId '"
- + NetworkIdentity.scrubSubscriberId(subscriberId) + "'.");
+ + NetworkIdentityUtils.scrubSubscriberId(subscriberId) + "'.");
}
return template;
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 72fb1ca..9f79dd0 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;
@@ -3595,6 +3588,24 @@
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_CROSS_LAYER_BLUR = "android.software.cross_layer_blur";
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+ * a Keystore implementation that can only enforce limited use key in hardware with max usage
+ * count equals to 1.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_KEYSTORE_SINGLE_USE_KEY =
+ "android.hardware.keystore.single_use_key";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+ * a Keystore implementation that can enforce limited use key in hardware with any max usage
+ * count (including count equals to 1).
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_KEYSTORE_LIMITED_USE_KEY =
+ "android.hardware.keystore.limited_use_key";
+
/** @hide */
public static final boolean APP_ENUMERATION_ENABLED_BY_DEFAULT = true;
@@ -6791,25 +6802,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/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
index 2650dc5..f097651 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
@@ -19,6 +19,8 @@
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC;
+import android.annotation.NonNull;
+import android.content.Context;
import android.hardware.biometrics.SensorProperties;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.os.Parcel;
@@ -81,10 +83,36 @@
@SensorProperties.Strength int strength, int maxEnrollmentsPerUser,
@FingerprintSensorProperties.SensorType int sensorType,
boolean resetLockoutRequiresHardwareAuthToken) {
- // TODO: Value should be provided from the HAL
+ // TODO(b/179175438): Value should be provided from the HAL
this(sensorId, strength, maxEnrollmentsPerUser, sensorType,
- resetLockoutRequiresHardwareAuthToken, 540 /* sensorLocationX */,
- 1769 /* sensorLocationY */, 130 /* sensorRadius */);
+ resetLockoutRequiresHardwareAuthToken, 0 /* sensorLocationX */,
+ 0 /* sensorLocationY */, 0 /* sensorRadius */);
+ }
+
+ /**
+ * Initializes SensorProperties with specified values and values obtained from resources using
+ * context.
+ */
+ // TODO(b/179175438): Remove this constructor once all HALs move to AIDL.
+ public FingerprintSensorPropertiesInternal(@NonNull Context context, int sensorId,
+ @SensorProperties.Strength int strength, int maxEnrollmentsPerUser,
+ @FingerprintSensorProperties.SensorType int sensorType,
+ boolean resetLockoutRequiresHardwareAuthToken) {
+ super(sensorId, strength, maxEnrollmentsPerUser);
+ this.sensorType = sensorType;
+ this.resetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken;
+
+ int[] props = context.getResources().getIntArray(
+ com.android.internal.R.array.config_udfps_sensor_props);
+ if (props != null && props.length == 3) {
+ this.sensorLocationX = props[0];
+ this.sensorLocationY = props[1];
+ this.sensorRadius = props[2];
+ } else {
+ this.sensorLocationX = 0;
+ this.sensorLocationY = 0;
+ this.sensorRadius = 0;
+ }
}
protected FingerprintSensorPropertiesInternal(Parcel in) {
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 06d0fc1..5d8122b 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -22,11 +22,12 @@
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.os.Build;
import android.service.NetworkIdentityProto;
import android.telephony.Annotation.NetworkType;
import android.util.proto.ProtoOutputStream;
+import com.android.net.module.util.NetworkIdentityUtils;
+
import java.util.Objects;
/**
@@ -90,7 +91,8 @@
builder.append(mSubType);
}
if (mSubscriberId != null) {
- builder.append(", subscriberId=").append(scrubSubscriberId(mSubscriberId));
+ builder.append(", subscriberId=")
+ .append(NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
}
if (mNetworkId != null) {
builder.append(", networkId=").append(mNetworkId);
@@ -111,7 +113,8 @@
// Not dumping mSubType, subtypes are no longer supported.
if (mSubscriberId != null) {
- proto.write(NetworkIdentityProto.SUBSCRIBER_ID, scrubSubscriberId(mSubscriberId));
+ proto.write(NetworkIdentityProto.SUBSCRIBER_ID,
+ NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
}
proto.write(NetworkIdentityProto.NETWORK_ID, mNetworkId);
proto.write(NetworkIdentityProto.ROAMING, mRoaming);
@@ -150,32 +153,6 @@
}
/**
- * Scrub given IMSI on production builds.
- */
- public static String scrubSubscriberId(String subscriberId) {
- if (Build.IS_ENG) {
- return subscriberId;
- } else if (subscriberId != null) {
- // TODO: parse this as MCC+MNC instead of hard-coding
- return subscriberId.substring(0, Math.min(6, subscriberId.length())) + "...";
- } else {
- return "null";
- }
- }
-
- /**
- * Scrub given IMSI on production builds.
- */
- public static String[] scrubSubscriberId(String[] subscriberId) {
- if (subscriberId == null) return null;
- final String[] res = new String[subscriberId.length];
- for (int i = 0; i < res.length; i++) {
- res[i] = NetworkIdentity.scrubSubscriberId(subscriberId[i]);
- }
- return res;
- }
-
- /**
* Build a {@link NetworkIdentity} from the given {@link NetworkState} and {@code subType},
* assuming that any mobile networks are using the current IMSI. The subType if applicable,
* should be set as one of the TelephonyManager.NETWORK_TYPE_* constants, or
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index dc33cc7..aa61e03 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -48,6 +48,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.net.module.util.NetworkIdentityUtils;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
@@ -296,11 +297,11 @@
builder.append("matchRule=").append(getMatchRuleName(mMatchRule));
if (mSubscriberId != null) {
builder.append(", subscriberId=").append(
- NetworkIdentity.scrubSubscriberId(mSubscriberId));
+ NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
}
if (mMatchSubscriberIds != null) {
builder.append(", matchSubscriberIds=").append(
- Arrays.toString(NetworkIdentity.scrubSubscriberId(mMatchSubscriberIds)));
+ Arrays.toString(NetworkIdentityUtils.scrubSubscriberIds(mMatchSubscriberIds)));
}
if (mNetworkId != null) {
builder.append(", networkId=").append(mNetworkId);
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/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 33beb6a..fa090f5 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -67,7 +67,6 @@
public class VcnManager {
@NonNull private static final String TAG = VcnManager.class.getSimpleName();
- /** @hide */
@VisibleForTesting
public static final Map<
VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index 2093077..217f178 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -268,7 +268,7 @@
}
/** @hide */
- public String usageToString(int usage) {
+ public static String usageToString(int usage) {
switch (usage) {
case USAGE_UNKNOWN:
return "UNKNOWN";
diff --git a/core/java/android/permission/Permissions.md b/core/java/android/permission/Permissions.md
index 4224b7a..dfe748b 100644
--- a/core/java/android/permission/Permissions.md
+++ b/core/java/android/permission/Permissions.md
@@ -809,6 +809,9 @@
permissions. It is unlikely that 3rd party apps will be able to use APIs protected by signature
permissions as they are usually not signed with the platform certificate.
+If possible, [role protected permissions](#role-protected-permissions) should also be considered as
+an alternative to better restrict which apps may get the permission.
+
Such permissions are defined and checked like an install time permission.
### Preinstalled permissions
@@ -819,6 +822,9 @@
Hence this permission level is discouraged unless there are
[further restrictions](#restricted-by-tests).
+If possible, [role protected permissions](#role-protected-permissions) should also be considered as
+an alternative to better restrict which apps may get the permission.
+
Such permissions are defined and checked like an install time permission.
### Privileged permissions
@@ -833,6 +839,9 @@
Hence this permission level is discouraged unless there are
[further restrictions](#restricted-by-tests).
+If possible, [role protected permissions](#role-protected-permissions) should also be considered as
+an alternative to better restrict which apps may get the permission.
+
Such permissions are defined and checked like an install time permission.
#### Restricted by tests
@@ -890,8 +899,16 @@
Which apps qualify for such a permission level is flexible and custom for each such level. Usually
they refer to a single or small set of apps, usually - but not always - apps defined in AOSP.
+This type of permission is deprecated in favor of
+[role protected permissions](#role-protected-permissions).
+
These permissions are defined and checked like an install time permission.
+### Role protected permissions
+
+See
+[Using role for permission protection](../../../../../../packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/role/RolePermissionProtection.md).
+
### Development permissions
> Not recommended
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f6ef8a3..9603f4d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5931,6 +5931,13 @@
public static final String ADAPTIVE_SLEEP = "adaptive_sleep";
/**
+ * Setting key to indicate whether camera-based autorotate is enabled.
+ *
+ * @hide
+ */
+ public static final String CAMERA_AUTOROTATE = "camera_autorotate";
+
+ /**
* @deprecated Use {@link android.provider.Settings.Global#DEVELOPMENT_SETTINGS_ENABLED}
* instead
*/
@@ -13573,6 +13580,28 @@
public static final String POWER_BUTTON_VERY_LONG_PRESS =
"power_button_very_long_press";
+
+ /**
+ * Keyguard should be on the left hand side of the screen, for wide screen layouts.
+ *
+ * @hide
+ */
+ public static final int ONE_HANDED_KEYGUARD_SIDE_LEFT = 0;
+
+ /**
+ * Keyguard should be on the right hand side of the screen, for wide screen layouts.
+ *
+ * @hide
+ */
+ public static final int ONE_HANDED_KEYGUARD_SIDE_RIGHT = 1;
+ /**
+ * In one handed mode, which side the keyguard should be on. Allowable values are one of
+ * the ONE_HANDED_KEYGUARD_SIDE_* constants.
+ *
+ * @hide
+ */
+ public static final String ONE_HANDED_KEYGUARD_SIDE = "one_handed_keyguard_side";
+
/**
* Keys we no longer back up under the current schema, but want to continue to
* process when restoring historical backup datasets.
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index f994d29..c39b8c5 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -80,6 +80,7 @@
public static final int KM_TAG_MIN_SECONDS_BETWEEN_OPS =
Tag.MIN_SECONDS_BETWEEN_OPS; // KM_UINT | 403;
public static final int KM_TAG_MAX_USES_PER_BOOT = Tag.MAX_USES_PER_BOOT; // KM_UINT | 404;
+ public static final int KM_TAG_USAGE_COUNT_LIMIT = Tag.USAGE_COUNT_LIMIT; // KM_UINT | 405;
public static final int KM_TAG_USER_ID = Tag.USER_ID; // KM_UINT | 501;
public static final int KM_TAG_USER_SECURE_ID = Tag.USER_SECURE_ID; // KM_ULONG_REP | 502;
diff --git a/core/java/android/service/smartspace/OWNERS b/core/java/android/service/smartspace/OWNERS
new file mode 100644
index 0000000..19ef9d7
--- /dev/null
+++ b/core/java/android/service/smartspace/OWNERS
@@ -0,0 +1,2 @@
+srazdan@google.com
+alexmang@google.com
\ No newline at end of file
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/Display.java b/core/java/android/view/Display.java
index c664ccb..4168064 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -25,8 +25,8 @@
import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.KeyguardManager;
+import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -59,12 +59,8 @@
* an application window, excluding the system decorations. The application display area may
* be smaller than the real display area because the system subtracts the space needed
* for decor elements such as the status bar. Use {@link WindowMetrics#getBounds()} to query the
- * application window bounds.</li>
- * <li>The real display area specifies the part of the display that contains content
- * including the system decorations. Even so, the real display area may be smaller than the
- * physical size of the display if the window manager is emulating a smaller display
- * using (adb shell wm size). Use the following methods to query the
- * real display area: {@link #getRealSize}, {@link #getRealMetrics}.</li>
+ * application window bounds. Generally, use {@link WindowManager#getCurrentWindowMetrics()} to
+ * query the metrics and perform UI-related actions.</li>
* </ul>
* </p><p>
* A logical display does not necessarily represent a particular physical display device
@@ -677,9 +673,9 @@
@UnsupportedAppUsage
public DisplayAdjustments getDisplayAdjustments() {
if (mResources != null) {
- final DisplayAdjustments currentAdjustements = mResources.getDisplayAdjustments();
- if (!mDisplayAdjustments.equals(currentAdjustements)) {
- mDisplayAdjustments = new DisplayAdjustments(currentAdjustements);
+ final DisplayAdjustments currentAdjustments = mResources.getDisplayAdjustments();
+ if (!mDisplayAdjustments.equals(currentAdjustments)) {
+ mDisplayAdjustments = new DisplayAdjustments(currentAdjustments);
}
}
@@ -900,6 +896,32 @@
}
/**
+ * Returns the {@link RoundedCorner} of the given position if there is one.
+ *
+ * @param position the position of the rounded corner on the display.
+ *
+ * @return the rounded corner of the given position. Returns {@code null} if there is none.
+ */
+ @SuppressLint("VisiblySynchronized")
+ @Nullable
+ public RoundedCorner getRoundedCorner(@RoundedCorner.Position int position) {
+ synchronized (this) {
+ updateDisplayInfoLocked();
+ RoundedCorners roundedCorners;
+ if (mMayAdjustByFixedRotation) {
+ roundedCorners = getDisplayAdjustments().adjustRoundedCorner(
+ mDisplayInfo.roundedCorners,
+ mDisplayInfo.rotation,
+ mDisplayInfo.logicalWidth,
+ mDisplayInfo.logicalHeight);
+ } else {
+ roundedCorners = mDisplayInfo.roundedCorners;
+ }
+ return roundedCorners == null ? null : roundedCorners.getRoundedCorner(position);
+ }
+ }
+
+ /**
* Gets the pixel format of the display.
* @return One of the constants defined in {@link android.graphics.PixelFormat}.
*
@@ -1191,30 +1213,34 @@
}
/**
- * Gets the real size of the display without subtracting any window decor or
- * applying any compatibility scale factors.
+ * Provides the largest {@link Point outSize} an app may expect in the current system state,
+ * without subtracting any window decor.
* <p>
- * The size is adjusted based on the current rotation of the display.
+ * The size describes the largest potential area the window might occupy. The size is adjusted
+ * based on the current rotation of the display.
* </p><p>
* The real size may be smaller than the physical size of the screen when the
* window manager is emulating a smaller display (using adb shell wm size).
- * </p><p>
- * In general, {@link #getRealSize(Point)} and {@link WindowManager#getMaximumWindowMetrics()}
- * report the same bounds except that certain areas of the display may not be available to
- * windows created in the {@link WindowManager}'s {@link Context}.
- *
- * For example, imagine a device which has a multi-task mode that limits windows to half of the
- * screen. In this case, {@link WindowManager#getMaximumWindowMetrics()} reports the
- * bounds of the screen half where the window is located, while {@link #getRealSize(Point)}
- * still reports the bounds of the whole display.
+ * </p>
*
* @param outSize Set to the real size of the display.
- *
- * @see WindowManager#getMaximumWindowMetrics()
*/
public void getRealSize(Point outSize) {
synchronized (this) {
updateDisplayInfoLocked();
+ if (shouldReportMaxBounds()) {
+ final Rect bounds = mResources.getConfiguration()
+ .windowConfiguration.getMaxBounds();
+ outSize.x = bounds.width();
+ outSize.y = bounds.height();
+ if (DEBUG) {
+ Log.d(TAG, "getRealSize determined from max bounds: " + outSize
+ + " for uid " + Process.myUid());
+ }
+ // Skip adjusting by fixed rotation, since if it is necessary, the configuration
+ // should already reflect the expected rotation.
+ return;
+ }
outSize.x = mDisplayInfo.logicalWidth;
outSize.y = mDisplayInfo.logicalHeight;
if (mMayAdjustByFixedRotation) {
@@ -1224,9 +1250,11 @@
}
/**
- * Gets display metrics based on the real size of this display.
+ * Provides the largest {@link DisplayMetrics outMetrics} an app may expect in the current
+ * system state, without subtracting any window decor.
* <p>
- * The size is adjusted based on the current rotation of the display.
+ * The size describes the largest potential area the window might occupy. The size is adjusted
+ * based on the current rotation of the display.
* </p><p>
* The real size may be smaller than the physical size of the screen when the
* window manager is emulating a smaller display (using adb shell wm size).
@@ -1237,6 +1265,18 @@
public void getRealMetrics(DisplayMetrics outMetrics) {
synchronized (this) {
updateDisplayInfoLocked();
+ if (shouldReportMaxBounds()) {
+ mDisplayInfo.getMaxBoundsMetrics(outMetrics,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO,
+ mResources.getConfiguration());
+ if (DEBUG) {
+ Log.d(TAG, "getRealMetrics determined from max bounds: " + outMetrics
+ + " for uid " + Process.myUid());
+ }
+ // Skip adjusting by fixed rotation, since if it is necessary, the configuration
+ // should already reflect the expected rotation.
+ return;
+ }
mDisplayInfo.getLogicalMetrics(outMetrics,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
if (mMayAdjustByFixedRotation) {
@@ -1246,6 +1286,20 @@
}
/**
+ * Determines if {@link WindowConfiguration#getMaxBounds()} should be reported as the
+ * display dimensions. The max bounds field may be smaller than the logical dimensions
+ * when apps need to be sandboxed.
+ * @return {@code true} when max bounds should be applied.
+ */
+ private boolean shouldReportMaxBounds() {
+ if (mResources == null) {
+ return false;
+ }
+ final Configuration config = mResources.getConfiguration();
+ return config != null && !config.windowConfiguration.getMaxBounds().isEmpty();
+ }
+
+ /**
* Gets the state of the display, such as whether it is on or off.
*
* @return The state of the display: one of {@link #STATE_OFF}, {@link #STATE_ON},
diff --git a/core/java/android/view/DisplayAdjustments.java b/core/java/android/view/DisplayAdjustments.java
index 5d5771c..e307eff 100644
--- a/core/java/android/view/DisplayAdjustments.java
+++ b/core/java/android/view/DisplayAdjustments.java
@@ -151,6 +151,23 @@
: realCutout;
}
+ /**
+ * Returns the adjusted {@link RoundedCorners} if available. Otherwise the original
+ * {@link RoundedCorners} is returned.
+ */
+ @Nullable
+ public RoundedCorners adjustRoundedCorner(@Nullable RoundedCorners realRoundedCorners,
+ @Surface.Rotation int realRotation, int displayWidth, int displayHeight) {
+ final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments;
+ if (realRoundedCorners == null || rotationAdjustments == null
+ || rotationAdjustments.mRotation == realRotation) {
+ return realRoundedCorners;
+ }
+
+ return realRoundedCorners.rotate(
+ rotationAdjustments.mRotation, displayWidth, displayHeight);
+ }
+
/** Returns the adjusted rotation if available. Otherwise the original rotation is returned. */
@Surface.Rotation
public int getRotation(@Surface.Rotation int realRotation) {
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index d200a328..8a44504 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -24,6 +24,7 @@
import static android.view.DisplayInfoProto.NAME;
import android.annotation.Nullable;
+import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -301,6 +302,12 @@
*/
public float brightnessDefault;
+ /**
+ * The {@link RoundedCorners} if present, otherwise {@code null}.
+ */
+ @Nullable
+ public RoundedCorners roundedCorners;
+
public static final @android.annotation.NonNull Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() {
@Override
public DisplayInfo createFromParcel(Parcel source) {
@@ -369,7 +376,8 @@
&& refreshRateOverride == other.refreshRateOverride
&& brightnessMinimum == other.brightnessMinimum
&& brightnessMaximum == other.brightnessMaximum
- && brightnessDefault == other.brightnessDefault;
+ && brightnessDefault == other.brightnessDefault
+ && Objects.equals(roundedCorners, other.roundedCorners);
}
@Override
@@ -418,6 +426,7 @@
brightnessMinimum = other.brightnessMinimum;
brightnessMaximum = other.brightnessMaximum;
brightnessDefault = other.brightnessDefault;
+ roundedCorners = other.roundedCorners;
}
public void readFromParcel(Parcel source) {
@@ -468,6 +477,7 @@
brightnessMinimum = source.readFloat();
brightnessMaximum = source.readFloat();
brightnessDefault = source.readFloat();
+ roundedCorners = source.readTypedObject(RoundedCorners.CREATOR);
}
@Override
@@ -517,6 +527,7 @@
dest.writeFloat(brightnessMinimum);
dest.writeFloat(brightnessMaximum);
dest.writeFloat(brightnessDefault);
+ dest.writeTypedObject(roundedCorners, flags);
}
@Override
@@ -605,11 +616,29 @@
getMetricsWithSize(outMetrics, ci, configuration, appWidth, appHeight);
}
+ /**
+ * Populates {@code outMetrics} with details of the logical display. Bounds are limited
+ * by the logical size of the display.
+ *
+ * @param outMetrics the {@link DisplayMetrics} to be populated
+ * @param compatInfo the {@link CompatibilityInfo} to be applied
+ * @param configuration the {@link Configuration}
+ */
public void getLogicalMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
Configuration configuration) {
getMetricsWithSize(outMetrics, compatInfo, configuration, logicalWidth, logicalHeight);
}
+ /**
+ * Similar to {@link #getLogicalMetrics}, but the limiting bounds are determined from
+ * {@link WindowConfiguration#getMaxBounds()}
+ */
+ public void getMaxBoundsMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
+ Configuration configuration) {
+ Rect bounds = configuration.windowConfiguration.getMaxBounds();
+ getMetricsWithSize(outMetrics, compatInfo, configuration, bounds.width(), bounds.height());
+ }
+
public int getNaturalWidth() {
return rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 ?
logicalWidth : logicalHeight;
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index b4e1172..e681c0e 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -653,6 +653,7 @@
private void updateState(InsetsState newState) {
mState.setDisplayFrame(newState.getDisplayFrame());
mState.setDisplayCutout(newState.getDisplayCutout());
+ mState.setRoundedCorners(newState.getRoundedCorners());
@InsetsType int disabledUserAnimationTypes = 0;
@InsetsType int[] cancelledUserAnimationTypes = {0};
for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index d68e903..fe6b6e4 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -173,6 +173,9 @@
private final DisplayCutout.ParcelableWrapper mDisplayCutout =
new DisplayCutout.ParcelableWrapper();
+ /** The rounded corners on the display */
+ private RoundedCorners mRoundedCorners = RoundedCorners.NO_ROUNDED_CORNERS;
+
public InsetsState() {
}
@@ -256,7 +259,8 @@
}
return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound,
- alwaysConsumeSystemBars, calculateRelativeCutout(frame), compatInsetsTypes,
+ alwaysConsumeSystemBars, calculateRelativeCutout(frame),
+ calculateRelativeRoundedCorners(frame), compatInsetsTypes,
(legacySystemUiFlags & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0);
}
@@ -281,6 +285,20 @@
return raw.inset(insetLeft, insetTop, insetRight, insetBottom);
}
+ private RoundedCorners calculateRelativeRoundedCorners(Rect frame) {
+ if (mDisplayFrame.equals(frame)) {
+ return mRoundedCorners;
+ }
+ if (frame == null) {
+ return RoundedCorners.NO_ROUNDED_CORNERS;
+ }
+ final int insetLeft = frame.left - mDisplayFrame.left;
+ final int insetTop = frame.top - mDisplayFrame.top;
+ final int insetRight = mDisplayFrame.right - frame.right;
+ final int insetBottom = mDisplayFrame.bottom - frame.bottom;
+ return mRoundedCorners.inset(insetLeft, insetTop, insetRight, insetBottom);
+ }
+
public Rect calculateInsets(Rect frame, @InsetsType int types, boolean ignoreVisibility) {
Insets insets = Insets.NONE;
for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
@@ -462,6 +480,14 @@
return mDisplayCutout.get();
}
+ public void setRoundedCorners(RoundedCorners roundedCorners) {
+ mRoundedCorners = roundedCorners;
+ }
+
+ public RoundedCorners getRoundedCorners() {
+ return mRoundedCorners;
+ }
+
/**
* Modifies the state of this class to exclude a certain type to make it ready for dispatching
* to the client.
@@ -493,6 +519,7 @@
public void scale(float scale) {
mDisplayFrame.scale(scale);
mDisplayCutout.scale(scale);
+ mRoundedCorners = mRoundedCorners.scale(scale);
for (int i = 0; i < SIZE; i++) {
final InsetsSource source = mSources[i];
if (source != null) {
@@ -512,6 +539,7 @@
public void set(InsetsState other, boolean copySources) {
mDisplayFrame.set(other.mDisplayFrame);
mDisplayCutout.set(other.mDisplayCutout);
+ mRoundedCorners = other.getRoundedCorners();
if (copySources) {
for (int i = 0; i < SIZE; i++) {
InsetsSource source = other.mSources[i];
@@ -619,11 +647,15 @@
}
public void dump(String prefix, PrintWriter pw) {
+ final String newPrefix = prefix + " ";
pw.println(prefix + "InsetsState");
+ pw.println(newPrefix + "mDisplayFrame=" + mDisplayFrame);
+ pw.println(newPrefix + "mDisplayCutout=" + mDisplayCutout.get());
+ pw.println(newPrefix + "mRoundedCorners=" + mRoundedCorners);
for (int i = 0; i < SIZE; i++) {
InsetsSource source = mSources[i];
if (source == null) continue;
- source.dump(prefix + " ", pw);
+ source.dump(newPrefix + " ", pw);
}
}
@@ -713,7 +745,8 @@
InsetsState state = (InsetsState) o;
if (!mDisplayFrame.equals(state.mDisplayFrame)
- || !mDisplayCutout.equals(state.mDisplayCutout)) {
+ || !mDisplayCutout.equals(state.mDisplayCutout)
+ || !mRoundedCorners.equals(state.mRoundedCorners)) {
return false;
}
for (int i = 0; i < SIZE; i++) {
@@ -737,7 +770,8 @@
@Override
public int hashCode() {
- return Objects.hash(mDisplayFrame, mDisplayCutout, Arrays.hashCode(mSources));
+ return Objects.hash(mDisplayFrame, mDisplayCutout, Arrays.hashCode(mSources),
+ mRoundedCorners);
}
public InsetsState(Parcel in) {
@@ -754,6 +788,7 @@
mDisplayFrame.writeToParcel(dest, flags);
mDisplayCutout.writeToParcel(dest, flags);
dest.writeParcelableArray(mSources, 0);
+ dest.writeTypedObject(mRoundedCorners, flags);
}
public static final @android.annotation.NonNull Creator<InsetsState> CREATOR = new Creator<InsetsState>() {
@@ -771,6 +806,7 @@
mDisplayFrame.set(Rect.CREATOR.createFromParcel(in));
mDisplayCutout.set(DisplayCutout.ParcelableWrapper.CREATOR.createFromParcel(in));
mSources = in.readParcelableArray(null, InsetsSource.class);
+ mRoundedCorners = in.readTypedObject(RoundedCorners.CREATOR);
}
@Override
@@ -785,6 +821,7 @@
return "InsetsState: {"
+ "mDisplayFrame=" + mDisplayFrame
+ ", mDisplayCutout=" + mDisplayCutout
+ + ", mRoundedCorners=" + mRoundedCorners
+ ", mSources= { " + joiner
+ " }";
}
diff --git a/core/java/android/view/RoundedCorner.java b/core/java/android/view/RoundedCorner.java
new file mode 100644
index 0000000..cc7525b
--- /dev/null
+++ b/core/java/android/view/RoundedCorner.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.graphics.Point;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Represents a rounded corner of the display.
+ *
+ * <code>
+ * ________
+ * / ^
+ * / | Radius
+ * | v
+ * | X <- Center point
+ * |<----->
+ * Radius
+ * </code>
+ *
+ * <p>Note: The rounded corner formed by the radius and the center is an approximation.</p>
+ *
+ * <p>{@link RoundedCorner} is immutable.</p>
+ */
+public final class RoundedCorner implements Parcelable {
+
+ /**
+ * The rounded corner is at the top-left of the screen.
+ */
+ public static final int POSITION_TOP_LEFT = 0;
+ /**
+ * The rounded corner is at the top-right of the screen.
+ */
+ public static final int POSITION_TOP_RIGHT = 1;
+ /**
+ * The rounded corner is at the bottom-right of the screen.
+ */
+ public static final int POSITION_BOTTOM_RIGHT = 2;
+ /**
+ * The rounded corner is at the bottom-left of the screen.
+ */
+ public static final int POSITION_BOTTOM_LEFT = 3;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "POSITION_" }, value = {
+ POSITION_TOP_LEFT,
+ POSITION_TOP_RIGHT,
+ POSITION_BOTTOM_RIGHT,
+ POSITION_BOTTOM_LEFT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Position {}
+
+ private final @Position int mPosition;
+ private final int mRadius;
+ @NonNull
+ private final Point mCenter;
+
+ /**
+ * Creates an empty {@link RoundedCorner} on the given position.
+ * @hide
+ */
+ @VisibleForTesting
+ public RoundedCorner(@Position int position) {
+ mPosition = position;
+ mRadius = 0;
+ mCenter = new Point(0, 0);
+ }
+
+ /**
+ * Creates a {@link RoundedCorner}.
+ *
+ * <p>Note that this is only useful for tests. For production code, developers should always
+ * use a {@link RoundedCorner} obtained from the system via
+ * {@link WindowInsets#getRoundedCorner} or {@link Display#getRoundedCorner}.</p>
+ *
+ * @param position the position of the rounded corner.
+ * @param radius the radius of the rounded corner.
+ * @param centerX the x of center point of the rounded corner.
+ * @param centerY the y of center point of the rounded corner.
+ *
+ */
+ public RoundedCorner(@Position int position, int radius, int centerX,
+ int centerY) {
+ mPosition = position;
+ mRadius = radius;
+ mCenter = new Point(centerX, centerY);
+ }
+
+ /**
+ * Creates a {@link RoundedCorner} from a passed in {@link RoundedCorner}.
+ *
+ * @hide
+ */
+ RoundedCorner(RoundedCorner rc) {
+ mPosition = rc.getPosition();
+ mRadius = rc.getRadius();
+ mCenter = new Point(rc.getCenter());
+ }
+
+ /**
+ * Get the position of this {@link RoundedCorner}.
+ *
+ * @see #POSITION_TOP_LEFT
+ * @see #POSITION_TOP_RIGHT
+ * @see #POSITION_BOTTOM_RIGHT
+ * @see #POSITION_BOTTOM_LEFT
+ */
+ public @Position int getPosition() {
+ return mPosition;
+ }
+
+ /**
+ * Returns the radius of a quarter circle approximation of this {@link RoundedCorner}.
+ *
+ * @return the rounded corner radius of this {@link RoundedCorner}. Returns 0 if there is no
+ * rounded corner.
+ */
+ public int getRadius() {
+ return mRadius;
+ }
+
+ /**
+ * Returns the circle center of a quarter circle approximation of this {@link RoundedCorner}.
+ *
+ * @return the center point of this {@link RoundedCorner} in the application's coordinate.
+ */
+ @NonNull
+ public Point getCenter() {
+ return new Point(mCenter);
+ }
+
+ /**
+ * Checks whether this {@link RoundedCorner} exists and is inside the application's bounds.
+ *
+ * @return {@code false} if there is a rounded corner and is contained in the application's
+ * bounds. Otherwise return {@code true}.
+ *
+ * @hide
+ */
+ public boolean isEmpty() {
+ return mRadius == 0 || mCenter.x == 0 || mCenter.y == 0;
+ }
+
+ private String getPositionString(@Position int position) {
+ switch (position) {
+ case POSITION_TOP_LEFT:
+ return "TopLeft";
+ case POSITION_TOP_RIGHT:
+ return "TopRight";
+ case POSITION_BOTTOM_RIGHT:
+ return "BottomRight";
+ case POSITION_BOTTOM_LEFT:
+ return "BottomLeft";
+ default:
+ return "Invalid";
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof RoundedCorner) {
+ RoundedCorner r = (RoundedCorner) o;
+ return mPosition == r.mPosition && mRadius == r.mRadius
+ && mCenter.equals(r.mCenter);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 0;
+ result = 31 * result + mPosition;
+ result = 31 * result + mRadius;
+ result = 31 * result + mCenter.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "RoundedCorner{"
+ + "position=" + getPositionString(mPosition)
+ + ", radius=" + mRadius
+ + ", center=" + mCenter
+ + '}';
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeInt(mPosition);
+ out.writeInt(mRadius);
+ out.writeInt(mCenter.x);
+ out.writeInt(mCenter.y);
+ }
+
+ public static final @NonNull Creator<RoundedCorner> CREATOR = new Creator<RoundedCorner>() {
+ @Override
+ public RoundedCorner createFromParcel(Parcel in) {
+ return new RoundedCorner(in.readInt(), in.readInt(), in.readInt(), in.readInt());
+ }
+
+ @Override
+ public RoundedCorner[] newArray(int size) {
+ return new RoundedCorner[size];
+ }
+ };
+}
diff --git a/core/java/android/view/RoundedCorners.aidl b/core/java/android/view/RoundedCorners.aidl
new file mode 100644
index 0000000..0a901c0
--- /dev/null
+++ b/core/java/android/view/RoundedCorners.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+parcelable RoundedCorners;
diff --git a/core/java/android/view/RoundedCorners.java b/core/java/android/view/RoundedCorners.java
new file mode 100644
index 0000000..015e804
--- /dev/null
+++ b/core/java/android/view/RoundedCorners.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
+import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
+import static android.view.RoundedCorner.POSITION_TOP_LEFT;
+import static android.view.RoundedCorner.POSITION_TOP_RIGHT;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Pair;
+import android.view.RoundedCorner.Position;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Arrays;
+
+/**
+ * A class to create & manage all the {@link RoundedCorner} on the display.
+ *
+ * @hide
+ */
+public class RoundedCorners implements Parcelable {
+
+ public static final RoundedCorners NO_ROUNDED_CORNERS = new RoundedCorners(
+ new RoundedCorner(POSITION_TOP_LEFT), new RoundedCorner(POSITION_TOP_RIGHT),
+ new RoundedCorner(POSITION_BOTTOM_RIGHT), new RoundedCorner(POSITION_BOTTOM_LEFT));
+
+ /**
+ * The number of possible positions at which rounded corners can be located.
+ */
+ public static final int ROUNDED_CORNER_POSITION_LENGTH = 4;
+
+ private static final Object CACHE_LOCK = new Object();
+
+ @GuardedBy("CACHE_LOCK")
+ private static int sCachedDisplayWidth;
+ @GuardedBy("CACHE_LOCK")
+ private static int sCachedDisplayHeight;
+ @GuardedBy("CACHE_LOCK")
+ private static Pair<Integer, Integer> sCachedRadii;
+ @GuardedBy("CACHE_LOCK")
+ private static RoundedCorners sCachedRoundedCorners;
+
+ @VisibleForTesting
+ public final RoundedCorner[] mRoundedCorners;
+
+ public RoundedCorners(RoundedCorner[] roundedCorners) {
+ mRoundedCorners = roundedCorners;
+ }
+
+ public RoundedCorners(RoundedCorner topLeft, RoundedCorner topRight, RoundedCorner bottomRight,
+ RoundedCorner bottomLeft) {
+ mRoundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ mRoundedCorners[POSITION_TOP_LEFT] = topLeft;
+ mRoundedCorners[POSITION_TOP_RIGHT] = topRight;
+ mRoundedCorners[POSITION_BOTTOM_RIGHT] = bottomRight;
+ mRoundedCorners[POSITION_BOTTOM_LEFT] = bottomLeft;
+ }
+
+ public RoundedCorners(RoundedCorners roundedCorners) {
+ mRoundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; ++i) {
+ mRoundedCorners[i] = new RoundedCorner(roundedCorners.mRoundedCorners[i]);
+ }
+ }
+
+ /**
+ * Creates the rounded corners according to @android:dimen/rounded_corner_radius,
+ * @android:dimen/rounded_corner_radius_top and @android:dimen/rounded_corner_radius_bottom
+ */
+ public static RoundedCorners fromResources(
+ Resources res, int displayWidth, int displayHeight) {
+ return fromRadii(loadRoundedCornerRadii(res), displayWidth, displayHeight);
+ }
+
+ /**
+ * Creates the rounded corners from radius
+ */
+ @VisibleForTesting
+ public static RoundedCorners fromRadii(Pair<Integer, Integer> radii, int displayWidth,
+ int displayHeight) {
+ if (radii == null) {
+ return null;
+ }
+
+ synchronized (CACHE_LOCK) {
+ if (radii.equals(sCachedRadii) && sCachedDisplayWidth == displayWidth
+ && sCachedDisplayHeight == displayHeight) {
+ return sCachedRoundedCorners;
+ }
+ }
+
+ final RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ final int topRadius = radii.first > 0 ? radii.first : 0;
+ final int bottomRadius = radii.second > 0 ? radii.second : 0;
+ for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; i++) {
+ roundedCorners[i] = createRoundedCorner(
+ i,
+ i <= POSITION_TOP_RIGHT ? topRadius : bottomRadius,
+ displayWidth,
+ displayHeight);
+ }
+
+ final RoundedCorners result = new RoundedCorners(roundedCorners);
+ synchronized (CACHE_LOCK) {
+ sCachedDisplayWidth = displayWidth;
+ sCachedDisplayHeight = displayHeight;
+ sCachedRadii = radii;
+ sCachedRoundedCorners = result;
+ }
+ return result;
+ }
+
+ /**
+ * Loads the rounded corner radii from resources.
+ *
+ * @param res
+ * @return a Pair of radius. The first is the top rounded corner radius and second is the
+ * bottom corner radius.
+ */
+ @Nullable
+ private static Pair<Integer, Integer> loadRoundedCornerRadii(Resources res) {
+ final int radiusDefault = res.getDimensionPixelSize(R.dimen.rounded_corner_radius);
+ final int radiusTop = res.getDimensionPixelSize(R.dimen.rounded_corner_radius_top);
+ final int radiusBottom = res.getDimensionPixelSize(R.dimen.rounded_corner_radius_bottom);
+ if (radiusDefault == 0 && radiusTop == 0 && radiusBottom == 0) {
+ return null;
+ }
+ final Pair<Integer, Integer> radii = new Pair<>(
+ radiusTop > 0 ? radiusTop : radiusDefault,
+ radiusBottom > 0 ? radiusBottom : radiusDefault);
+ return radii;
+ }
+
+ /**
+ * Insets the reference frame of the rounded corners.
+ *
+ * @return a copy of this instance which has been inset
+ */
+ public RoundedCorners inset(int insetLeft, int insetTop, int insetRight, int insetBottom) {
+ final RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; i++) {
+ roundedCorners[i] = insetRoundedCorner(i, insetLeft, insetTop, insetRight, insetBottom);
+ }
+ return new RoundedCorners(roundedCorners);
+ }
+
+ private RoundedCorner insetRoundedCorner(@Position int position, int insetLeft,
+ int insetTop, int insetRight, int insetBottom) {
+ if (mRoundedCorners[position].isEmpty()) {
+ return new RoundedCorner(position);
+ }
+
+ final int radius = mRoundedCorners[position].getRadius();
+ final Point center = mRoundedCorners[position].getCenter();
+ boolean hasRoundedCorner;
+ switch (position) {
+ case POSITION_TOP_LEFT:
+ hasRoundedCorner = radius > insetTop || radius > insetLeft;
+ break;
+ case POSITION_TOP_RIGHT:
+ hasRoundedCorner = radius > insetTop || radius > insetRight;
+ break;
+ case POSITION_BOTTOM_RIGHT:
+ hasRoundedCorner = radius > insetBottom || radius > insetRight;
+ break;
+ case POSITION_BOTTOM_LEFT:
+ hasRoundedCorner = radius > insetBottom || radius > insetLeft;
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "The position is not one of the RoundedCornerPosition =" + position);
+ }
+ return new RoundedCorner(
+ position, radius,
+ hasRoundedCorner ? center.x - insetLeft : 0,
+ hasRoundedCorner ? center.y - insetTop : 0);
+ }
+
+ /**
+ * Returns the {@link RoundedCorner} of the given position if there is one.
+ *
+ * @param position the position of the rounded corner on the display.
+ * @return the rounded corner of the given position. Returns {@code null} if
+ * {@link RoundedCorner#isEmpty()} is {@code true}.
+ */
+ @Nullable
+ public RoundedCorner getRoundedCorner(@Position int position) {
+ return mRoundedCorners[position].isEmpty()
+ ? null : new RoundedCorner(mRoundedCorners[position]);
+ }
+
+ /**
+ * Sets the rounded corner of given position.
+ *
+ * @param position the position of this rounded corner
+ * @param roundedCorner the rounded corner or null if there is none
+ */
+ public void setRoundedCorner(@Position int position, @Nullable RoundedCorner roundedCorner) {
+ mRoundedCorners[position] = roundedCorner == null
+ ? new RoundedCorner(position) : roundedCorner;
+ }
+
+ /**
+ * Returns an array of {@link RoundedCorner}s. Ordinal value of RoundedCornerPosition is used
+ * as an index of the array.
+ *
+ * @return an array of {@link RoundedCorner}s, one for each rounded corner area.
+ */
+ public RoundedCorner[] getAllRoundedCorners() {
+ RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; ++i) {
+ roundedCorners[i] = new RoundedCorner(roundedCorners[i]);
+ }
+ return roundedCorners;
+ }
+
+ /**
+ * Returns a scaled RoundedCorners.
+ */
+ public RoundedCorners scale(float scale) {
+ if (scale == 1f) {
+ return this;
+ }
+
+ RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; ++i) {
+ final RoundedCorner roundedCorner = mRoundedCorners[i];
+ roundedCorners[i] = new RoundedCorner(
+ i,
+ (int) (roundedCorner.getRadius() * scale),
+ (int) (roundedCorner.getCenter().x * scale),
+ (int) (roundedCorner.getCenter().y * scale));
+ }
+ return new RoundedCorners(roundedCorners);
+ }
+
+ /**
+ * Returns a rotated RoundedCorners.
+ */
+ public RoundedCorners rotate(@Surface.Rotation int rotation, int initialDisplayWidth,
+ int initialDisplayHeight) {
+ if (rotation == ROTATION_0) {
+ return this;
+ }
+ final boolean isSizeFlipped = rotation == ROTATION_90 || rotation == ROTATION_270;
+ RoundedCorner[] newCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ int newPosistion;
+ for (int i = 0; i < mRoundedCorners.length; i++) {
+ newPosistion = getRotatedIndex(i, rotation);
+ newCorners[newPosistion] = createRoundedCorner(
+ newPosistion,
+ mRoundedCorners[i].getRadius(),
+ isSizeFlipped ? initialDisplayHeight : initialDisplayWidth,
+ isSizeFlipped ? initialDisplayWidth : initialDisplayHeight);
+ }
+ return new RoundedCorners(newCorners);
+ }
+
+ private static RoundedCorner createRoundedCorner(@Position int position,
+ int radius, int displayWidth, int displayHeight) {
+ switch (position) {
+ case POSITION_TOP_LEFT:
+ return new RoundedCorner(
+ POSITION_TOP_LEFT,
+ radius,
+ radius > 0 ? radius : 0,
+ radius > 0 ? radius : 0);
+ case POSITION_TOP_RIGHT:
+ return new RoundedCorner(
+ POSITION_TOP_RIGHT,
+ radius,
+ radius > 0 ? displayWidth - radius : 0,
+ radius > 0 ? radius : 0);
+ case POSITION_BOTTOM_RIGHT:
+ return new RoundedCorner(
+ POSITION_BOTTOM_RIGHT,
+ radius,
+ radius > 0 ? displayWidth - radius : 0,
+ radius > 0 ? displayHeight - radius : 0);
+ case POSITION_BOTTOM_LEFT:
+ return new RoundedCorner(
+ POSITION_BOTTOM_LEFT,
+ radius,
+ radius > 0 ? radius : 0,
+ radius > 0 ? displayHeight - radius : 0);
+ default:
+ throw new IllegalArgumentException(
+ "The position is not one of the RoundedCornerPosition =" + position);
+ }
+ }
+
+ private static int getRotatedIndex(int position, int rotation) {
+ return (position - rotation + ROUNDED_CORNER_POSITION_LENGTH) % 4;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 0;
+ for (RoundedCorner roundedCorner : mRoundedCorners) {
+ result = result * 31 + roundedCorner.hashCode();
+ }
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof RoundedCorners) {
+ RoundedCorners r = (RoundedCorners) o;
+ return Arrays.deepEquals(mRoundedCorners, ((RoundedCorners) o).mRoundedCorners);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "RoundedCorners{" + Arrays.toString(mRoundedCorners) + "}";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (equals(NO_ROUNDED_CORNERS)) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ dest.writeTypedArray(mRoundedCorners, flags);
+ }
+ }
+
+ public static final @NonNull Creator<RoundedCorners> CREATOR = new Creator<RoundedCorners>() {
+ @Override
+ public RoundedCorners createFromParcel(Parcel in) {
+ int variant = in.readInt();
+ if (variant == 0) {
+ return NO_ROUNDED_CORNERS;
+ }
+ RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ in.readTypedArray(roundedCorners, RoundedCorner.CREATOR);
+ return new RoundedCorners(roundedCorners);
+ }
+
+ @Override
+ public RoundedCorners[] newArray(int size) {
+ return new RoundedCorners[size];
+ }
+ };
+}
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/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 7a5561c..41c38a1 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -82,6 +82,7 @@
@Nullable private Rect mTempRect;
private final boolean mIsRound;
@Nullable private final DisplayCutout mDisplayCutout;
+ @Nullable private final RoundedCorners mRoundedCorners;
/**
* In multi-window we force show the navigation bar. Because we don't want that the surface size
@@ -125,11 +126,12 @@
* @hide
* @deprecated Use {@link WindowInsets(SparseArray, SparseArray, boolean, boolean, DisplayCutout)}
*/
- public WindowInsets(Rect systemWindowInsetsRect, Rect stableInsetsRect,
- boolean isRound, boolean alwaysConsumeSystemBars, DisplayCutout displayCutout) {
+ @Deprecated
+ public WindowInsets(Rect systemWindowInsetsRect, Rect stableInsetsRect, boolean isRound,
+ boolean alwaysConsumeSystemBars, DisplayCutout displayCutout) {
this(createCompatTypeMap(systemWindowInsetsRect), createCompatTypeMap(stableInsetsRect),
createCompatVisibilityMap(createCompatTypeMap(systemWindowInsetsRect)),
- isRound, alwaysConsumeSystemBars, displayCutout, systemBars(),
+ isRound, alwaysConsumeSystemBars, displayCutout, null, systemBars(),
false /* compatIgnoreVisibility */);
}
@@ -150,7 +152,8 @@
boolean[] typeVisibilityMap,
boolean isRound,
boolean alwaysConsumeSystemBars, DisplayCutout displayCutout,
- @InsetsType int compatInsetsTypes, boolean compatIgnoreVisibility) {
+ RoundedCorners roundedCorners, @InsetsType int compatInsetsTypes,
+ boolean compatIgnoreVisibility) {
mSystemWindowInsetsConsumed = typeInsetsMap == null;
mTypeInsetsMap = mSystemWindowInsetsConsumed
? new Insets[SIZE]
@@ -170,6 +173,8 @@
mDisplayCutoutConsumed = displayCutout == null;
mDisplayCutout = (mDisplayCutoutConsumed || displayCutout.isEmpty())
? null : displayCutout;
+
+ mRoundedCorners = roundedCorners;
}
/**
@@ -182,6 +187,7 @@
src.mStableInsetsConsumed ? null : src.mTypeMaxInsetsMap,
src.mTypeVisibilityMap, src.mIsRound,
src.mAlwaysConsumeSystemBars, displayCutoutCopyConstructorArgument(src),
+ src.mRoundedCorners,
src.mCompatInsetsTypes,
src.mCompatIgnoreVisibility);
}
@@ -235,7 +241,7 @@
@UnsupportedAppUsage
public WindowInsets(Rect systemWindowInsets) {
this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, false, null,
- systemBars(), false /* compatIgnoreVisibility */);
+ null, systemBars(), false /* compatIgnoreVisibility */);
}
/**
@@ -466,7 +472,7 @@
public boolean hasInsets() {
return !getInsets(mTypeInsetsMap, all()).equals(Insets.NONE)
|| !getInsets(mTypeMaxInsetsMap, all()).equals(Insets.NONE)
- || mDisplayCutout != null;
+ || mDisplayCutout != null || mRoundedCorners != null;
}
/**
@@ -487,6 +493,23 @@
}
/**
+ * Returns the {@link RoundedCorner} of the given position if there is one.
+ *
+ * @param position the position of the rounded corner on the display. The value should be one of
+ * the following:
+ * {@link RoundedCorner#POSITION_TOP_LEFT},
+ * {@link RoundedCorner#POSITION_TOP_RIGHT},
+ * {@link RoundedCorner#POSITION_BOTTOM_RIGHT},
+ * {@link RoundedCorner#POSITION_BOTTOM_LEFT}.
+ * @return the rounded corner of the given position. Returns {@code null} if there is none or
+ * the rounded corner area is not inside the application's bounds.
+ */
+ @Nullable
+ public RoundedCorner getRoundedCorner(@RoundedCorner.Position int position) {
+ return mRoundedCorners == null ? null : mRoundedCorners.getRoundedCorner(position);
+ }
+
+ /**
* Returns a copy of this WindowInsets with the cutout fully consumed.
*
* @return A modified copy of this WindowInsets
@@ -501,7 +524,7 @@
mStableInsetsConsumed ? null : mTypeMaxInsetsMap,
mTypeVisibilityMap,
mIsRound, mAlwaysConsumeSystemBars,
- null /* displayCutout */,
+ null /* displayCutout */, mRoundedCorners,
mCompatInsetsTypes, mCompatIgnoreVisibility);
}
@@ -553,7 +576,7 @@
mTypeVisibilityMap,
mIsRound, mAlwaysConsumeSystemBars,
displayCutoutCopyConstructorArgument(this),
- mCompatInsetsTypes, mCompatIgnoreVisibility);
+ mRoundedCorners, mCompatInsetsTypes, mCompatIgnoreVisibility);
}
// TODO(b/119190588): replace @code with @link below
@@ -856,6 +879,8 @@
result.append(mDisplayCutout != null ? "cutout=" + mDisplayCutout : "");
result.append("\n ");
+ result.append(mRoundedCorners != null ? "roundedCorners=" + mRoundedCorners : "");
+ result.append("\n ");
result.append(isRound() ? "round" : "");
result.append("}");
return result.toString();
@@ -947,6 +972,9 @@
: mDisplayCutout == null
? DisplayCutout.NO_CUTOUT
: mDisplayCutout.inset(left, top, right, bottom),
+ mRoundedCorners == null
+ ? RoundedCorners.NO_ROUNDED_CORNERS
+ : mRoundedCorners.inset(left, top, right, bottom),
mCompatInsetsTypes, mCompatIgnoreVisibility);
}
@@ -964,13 +992,14 @@
&& Arrays.equals(mTypeInsetsMap, that.mTypeInsetsMap)
&& Arrays.equals(mTypeMaxInsetsMap, that.mTypeMaxInsetsMap)
&& Arrays.equals(mTypeVisibilityMap, that.mTypeVisibilityMap)
- && Objects.equals(mDisplayCutout, that.mDisplayCutout);
+ && Objects.equals(mDisplayCutout, that.mDisplayCutout)
+ && Objects.equals(mRoundedCorners, that.mRoundedCorners);
}
@Override
public int hashCode() {
return Objects.hash(Arrays.hashCode(mTypeInsetsMap), Arrays.hashCode(mTypeMaxInsetsMap),
- Arrays.hashCode(mTypeVisibilityMap), mIsRound, mDisplayCutout,
+ Arrays.hashCode(mTypeVisibilityMap), mIsRound, mDisplayCutout, mRoundedCorners,
mAlwaysConsumeSystemBars, mSystemWindowInsetsConsumed, mStableInsetsConsumed,
mDisplayCutoutConsumed);
}
@@ -1032,6 +1061,7 @@
private boolean mStableInsetsConsumed = true;
private DisplayCutout mDisplayCutout;
+ private RoundedCorners mRoundedCorners = RoundedCorners.NO_ROUNDED_CORNERS;
private boolean mIsRound;
private boolean mAlwaysConsumeSystemBars;
@@ -1057,6 +1087,7 @@
mSystemInsetsConsumed = insets.mSystemWindowInsetsConsumed;
mStableInsetsConsumed = insets.mStableInsetsConsumed;
mDisplayCutout = displayCutoutCopyConstructorArgument(insets);
+ mRoundedCorners = insets.mRoundedCorners;
mIsRound = insets.mIsRound;
mAlwaysConsumeSystemBars = insets.mAlwaysConsumeSystemBars;
}
@@ -1262,6 +1293,29 @@
/** @hide */
@NonNull
+ public Builder setRoundedCorners(RoundedCorners roundedCorners) {
+ mRoundedCorners = roundedCorners != null
+ ? roundedCorners : RoundedCorners.NO_ROUNDED_CORNERS;
+ return this;
+ }
+
+ /**
+ * Sets the rounded corner of given position.
+ *
+ * @see #getRoundedCorner(int)
+ * @param position the position of this rounded corner
+ * @param roundedCorner the rounded corner or null if there is none
+ * @return itself
+ */
+ @NonNull
+ public Builder setRoundedCorner(@RoundedCorner.Position int position,
+ @Nullable RoundedCorner roundedCorner) {
+ mRoundedCorners.setRoundedCorner(position, roundedCorner);
+ return this;
+ }
+
+ /** @hide */
+ @NonNull
public Builder setRound(boolean round) {
mIsRound = round;
return this;
@@ -1283,7 +1337,7 @@
public WindowInsets build() {
return new WindowInsets(mSystemInsetsConsumed ? null : mTypeInsetsMap,
mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mTypeVisibilityMap,
- mIsRound, mAlwaysConsumeSystemBars, mDisplayCutout,
+ mIsRound, mAlwaysConsumeSystemBars, mDisplayCutout, mRoundedCorners,
systemBars(), false /* compatIgnoreVisibility */);
}
}
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/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index 950dc73..c7eac6c 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -218,4 +218,15 @@
public String getDataDirectorySuffix() {
return WebViewFactory.getDataDirectorySuffix();
}
+
+ /**
+ * Returns an array of startup timestamps. For the specification of array
+ * see {@link WebViewFactory.Timestamp}.
+ * This method must be called on the same thread where the
+ * WebViewChromiumFactoryProvider#create method was invoked.
+ */
+ @NonNull
+ public long[] getTimestamps() {
+ return WebViewFactory.getTimestamps();
+ }
}
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index b91e7d3..2e75834 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -16,6 +16,8 @@
package android.webkit;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.app.ActivityManager;
import android.app.AppGlobals;
@@ -35,6 +37,8 @@
import android.util.Log;
import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
/**
@@ -67,6 +71,33 @@
private static boolean sWebViewDisabled;
private static String sDataDirectorySuffix; // stored here so it can be set without loading WV
+ // Indices in sTimestamps array.
+ /** @hide */
+ @IntDef(value = {
+ WEBVIEW_LOAD_START, CREATE_CONTEXT_START, CREATE_CONTEXT_END,
+ ADD_ASSETS_START, ADD_ASSETS_END, GET_CLASS_LOADER_START, GET_CLASS_LOADER_END,
+ NATIVE_LOAD_START, NATIVE_LOAD_END,
+ PROVIDER_CLASS_FOR_NAME_START, PROVIDER_CLASS_FOR_NAME_END})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Timestamp {
+ }
+
+ public static final int WEBVIEW_LOAD_START = 0;
+ public static final int CREATE_CONTEXT_START = 1;
+ public static final int CREATE_CONTEXT_END = 2;
+ public static final int ADD_ASSETS_START = 3;
+ public static final int ADD_ASSETS_END = 4;
+ public static final int GET_CLASS_LOADER_START = 5;
+ public static final int GET_CLASS_LOADER_END = 6;
+ public static final int NATIVE_LOAD_START = 7;
+ public static final int NATIVE_LOAD_END = 8;
+ public static final int PROVIDER_CLASS_FOR_NAME_START = 9;
+ public static final int PROVIDER_CLASS_FOR_NAME_END = 10;
+ private static final int TIMESTAMPS_SIZE = 11;
+
+ // WebView startup timestamps. To access elements use {@link Timestamp}.
+ private static long[] sTimestamps = new long[TIMESTAMPS_SIZE];
+
// Error codes for loadWebViewNativeLibraryFromPackage
public static final int LIBLOAD_SUCCESS = 0;
public static final int LIBLOAD_WRONG_PACKAGE_NAME = 1;
@@ -230,6 +261,7 @@
// us honest and minimize usage of WebView internals when binding the proxy.
if (sProviderInstance != null) return sProviderInstance;
+ sTimestamps[WEBVIEW_LOAD_START] = System.currentTimeMillis();
final int uid = android.os.Process.myUid();
if (uid == android.os.Process.ROOT_UID || uid == android.os.Process.SYSTEM_UID
|| uid == android.os.Process.PHONE_UID || uid == android.os.Process.NFC_UID
@@ -369,6 +401,7 @@
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
"initialApplication.createApplicationContext");
+ sTimestamps[CREATE_CONTEXT_START] = System.currentTimeMillis();
try {
// Construct an app context to load the Java code into the current app.
Context webViewContext = initialApplication.createApplicationContext(
@@ -377,6 +410,7 @@
sPackageInfo = newPackageInfo;
return webViewContext;
} finally {
+ sTimestamps[CREATE_CONTEXT_END] = System.currentTimeMillis();
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
} catch (RemoteException | PackageManager.NameNotFoundException e) {
@@ -402,20 +436,26 @@
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()");
try {
+ sTimestamps[ADD_ASSETS_START] = System.currentTimeMillis();
for (String newAssetPath : webViewContext.getApplicationInfo().getAllApkPaths()) {
initialApplication.getAssets().addAssetPathAsSharedLibrary(newAssetPath);
}
+ sTimestamps[ADD_ASSETS_END] = sTimestamps[GET_CLASS_LOADER_START] =
+ System.currentTimeMillis();
ClassLoader clazzLoader = webViewContext.getClassLoader();
-
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
+ sTimestamps[GET_CLASS_LOADER_END] = sTimestamps[NATIVE_LOAD_START] =
+ System.currentTimeMillis();
WebViewLibraryLoader.loadNativeLibrary(clazzLoader,
getWebViewLibrary(sPackageInfo.applicationInfo));
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
-
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
+ sTimestamps[NATIVE_LOAD_END] = sTimestamps[PROVIDER_CLASS_FOR_NAME_START] =
+ System.currentTimeMillis();
try {
return getWebViewProviderClass(clazzLoader);
} finally {
+ sTimestamps[PROVIDER_CLASS_FOR_NAME_END] = System.currentTimeMillis();
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
} catch (ClassNotFoundException e) {
@@ -477,4 +517,9 @@
return IWebViewUpdateService.Stub.asInterface(
ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME));
}
+
+ @NonNull
+ static long[] getTimestamps() {
+ return sTimestamps;
+ }
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 26dd5e3..012352d 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1705,15 +1705,23 @@
}
private void updateFloatingToolbarVisibility(MotionEvent event) {
- if (mTextActionMode != null) {
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_MOVE:
- hideFloatingToolbar(ActionMode.DEFAULT_HIDE_DURATION);
- break;
- case MotionEvent.ACTION_UP: // fall through
- case MotionEvent.ACTION_CANCEL:
+ if (mTextActionMode == null) {
+ return;
+ }
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_MOVE:
+ hideFloatingToolbar(ActionMode.DEFAULT_HIDE_DURATION);
+ break;
+ case MotionEvent.ACTION_UP: // fall through
+ case MotionEvent.ACTION_CANCEL:
+ final SelectionModifierCursorController selectionController =
+ getSelectionController();
+ final InsertionPointCursorController insertionController = getInsertionController();
+ if ((selectionController != null && selectionController.isCursorBeingModified())
+ || (insertionController != null
+ && insertionController.isCursorBeingModified())) {
showFloatingToolbar();
- }
+ }
}
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 8dafc5d..b47a0ac 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -17,6 +17,7 @@
package android.widget;
import android.annotation.ColorInt;
+import android.annotation.ColorRes;
import android.annotation.DimenRes;
import android.annotation.DrawableRes;
import android.annotation.IdRes;
@@ -25,6 +26,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Px;
+import android.annotation.StringRes;
import android.annotation.StyleRes;
import android.app.Activity;
import android.app.ActivityOptions;
@@ -63,6 +65,7 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.DisplayMetrics;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
@@ -180,6 +183,8 @@
private static final int SET_RIPPLE_DRAWABLE_COLOR_TAG = 21;
private static final int SET_INT_TAG_TAG = 22;
private static final int REMOVE_FROM_PARENT_ACTION_TAG = 23;
+ private static final int RESOURCE_REFLECTION_ACTION_TAG = 24;
+ private static final int COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG = 25;
/** @hide **/
@IntDef(prefix = "MARGIN_", value = {
@@ -980,6 +985,45 @@
return rect;
}
+ private static Class<?> getParameterType(int type) {
+ switch (type) {
+ case BaseReflectionAction.BOOLEAN:
+ return boolean.class;
+ case BaseReflectionAction.BYTE:
+ return byte.class;
+ case BaseReflectionAction.SHORT:
+ return short.class;
+ case BaseReflectionAction.INT:
+ return int.class;
+ case BaseReflectionAction.LONG:
+ return long.class;
+ case BaseReflectionAction.FLOAT:
+ return float.class;
+ case BaseReflectionAction.DOUBLE:
+ return double.class;
+ case BaseReflectionAction.CHAR:
+ return char.class;
+ case BaseReflectionAction.STRING:
+ return String.class;
+ case BaseReflectionAction.CHAR_SEQUENCE:
+ return CharSequence.class;
+ case BaseReflectionAction.URI:
+ return Uri.class;
+ case BaseReflectionAction.BITMAP:
+ return Bitmap.class;
+ case BaseReflectionAction.BUNDLE:
+ return Bundle.class;
+ case BaseReflectionAction.INTENT:
+ return Intent.class;
+ case BaseReflectionAction.COLOR_STATE_LIST:
+ return ColorStateList.class;
+ case BaseReflectionAction.ICON:
+ return Icon.class;
+ default:
+ return null;
+ }
+ }
+
private MethodHandle getMethod(View view, String methodName, Class<?> paramType,
boolean async) {
MethodArgs result;
@@ -1282,7 +1326,8 @@
@Override
public void apply(View root, ViewGroup rootParent,
OnClickHandler handler) throws ActionException {
- ReflectionAction ra = new ReflectionAction(viewId, methodName, ReflectionAction.BITMAP,
+ ReflectionAction ra = new ReflectionAction(viewId, methodName,
+ BaseReflectionAction.BITMAP,
bitmap);
ra.apply(root, rootParent, handler);
}
@@ -1301,7 +1346,7 @@
/**
* Base class for the reflection actions.
*/
- private final class ReflectionAction extends Action {
+ private abstract class BaseReflectionAction extends Action {
static final int BOOLEAN = 1;
static final int BYTE = 2;
static final int SHORT = 3;
@@ -1324,17 +1369,14 @@
@UnsupportedAppUsage
String methodName;
int type;
- @UnsupportedAppUsage
- Object value;
- ReflectionAction(@IdRes int viewId, String methodName, int type, Object value) {
+ BaseReflectionAction(@IdRes int viewId, String methodName, int type) {
this.viewId = viewId;
this.methodName = methodName;
this.type = type;
- this.value = value;
}
- ReflectionAction(Parcel in) {
+ BaseReflectionAction(Parcel in) {
this.viewId = in.readInt();
this.methodName = in.readString8();
this.type = in.readInt();
@@ -1343,7 +1385,125 @@
Log.d(LOG_TAG, "read viewId=0x" + Integer.toHexString(this.viewId)
+ " methodName=" + this.methodName + " type=" + this.type);
}
+ }
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(this.viewId);
+ out.writeString8(this.methodName);
+ out.writeInt(this.type);
+ }
+
+ /**
+ * Returns the value to use as parameter for the method.
+ *
+ * The view might be passed as {@code null} if the parameter value is requested outside of
+ * inflation. If the parameter cannot be determined at that time, the method should return
+ * {@code null} but not raise any exception.
+ */
+ @Nullable
+ protected abstract Object getParameterValue(@Nullable View view) throws ActionException;
+
+ @Override
+ public final void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ final View view = root.findViewById(viewId);
+ if (view == null) return;
+
+ Class<?> param = getParameterType(this.type);
+ if (param == null) {
+ throw new ActionException("bad type: " + this.type);
+ }
+ Object value = getParameterValue(view);
+ try {
+ getMethod(view, this.methodName, param, false /* async */).invoke(view, value);
+ } catch (Throwable ex) {
+ throw new ActionException(ex);
+ }
+ }
+
+ @Override
+ public final Action initActionAsync(ViewTree root, ViewGroup rootParent,
+ OnClickHandler handler) {
+ final View view = root.findViewById(viewId);
+ if (view == null) return ACTION_NOOP;
+
+ Class<?> param = getParameterType(this.type);
+ if (param == null) {
+ throw new ActionException("bad type: " + this.type);
+ }
+
+ Object value = getParameterValue(view);
+ try {
+ MethodHandle method = getMethod(view, this.methodName, param, true /* async */);
+
+ if (method != null) {
+ Runnable endAction = (Runnable) method.invoke(view, value);
+ if (endAction == null) {
+ return ACTION_NOOP;
+ }
+ // Special case view stub
+ if (endAction instanceof ViewStub.ViewReplaceRunnable) {
+ root.createTree();
+ // Replace child tree
+ root.findViewTreeById(viewId).replaceView(
+ ((ViewStub.ViewReplaceRunnable) endAction).view);
+ }
+ return new RunnableAction(endAction);
+ }
+ } catch (Throwable ex) {
+ throw new ActionException(ex);
+ }
+
+ return this;
+ }
+
+ public final int mergeBehavior() {
+ // smoothScrollBy is cumulative, everything else overwites.
+ if (methodName.equals("smoothScrollBy")) {
+ return MERGE_APPEND;
+ } else {
+ return MERGE_REPLACE;
+ }
+ }
+
+ @Override
+ public final String getUniqueKey() {
+ // Each type of reflection action corresponds to a setter, so each should be seen as
+ // unique from the standpoint of merging.
+ return super.getUniqueKey() + this.methodName + this.type;
+ }
+
+ @Override
+ public final boolean prefersAsyncApply() {
+ return this.type == URI || this.type == ICON;
+ }
+
+ @Override
+ public final void visitUris(@NonNull Consumer<Uri> visitor) {
+ switch (this.type) {
+ case URI:
+ final Uri uri = (Uri) getParameterValue(null);
+ if (uri != null) visitor.accept(uri);
+ break;
+ case ICON:
+ final Icon icon = (Icon) getParameterValue(null);
+ if (icon != null) visitIconUri(icon, visitor);
+ break;
+ }
+ }
+ }
+
+ /** Class for the reflection actions. */
+ private final class ReflectionAction extends BaseReflectionAction {
+ @UnsupportedAppUsage
+ Object value;
+
+ ReflectionAction(@IdRes int viewId, String methodName, int type, Object value) {
+ super(viewId, methodName, type);
+ this.value = value;
+ }
+
+ ReflectionAction(Parcel in) {
+ super(in);
// For some values that may have been null, we first check a flag to see if they were
// written to the parcel.
switch (this.type) {
@@ -1354,7 +1514,7 @@
this.value = in.readByte();
break;
case SHORT:
- this.value = (short)in.readInt();
+ this.value = (short) in.readInt();
break;
case INT:
this.value = in.readInt();
@@ -1369,7 +1529,7 @@
this.value = in.readDouble();
break;
case CHAR:
- this.value = (char)in.readInt();
+ this.value = (char) in.readInt();
break;
case STRING:
this.value = in.readString8();
@@ -1400,15 +1560,7 @@
}
public void writeToParcel(Parcel out, int flags) {
- out.writeInt(this.viewId);
- out.writeString8(this.methodName);
- out.writeInt(this.type);
- //noinspection ConstantIfStatement
- if (false) {
- Log.d(LOG_TAG, "write viewId=0x" + Integer.toHexString(this.viewId)
- + " methodName=" + this.methodName + " type=" + this.type);
- }
-
+ super.writeToParcel(out, flags);
// For some values which are null, we record an integer flag to indicate whether
// we have written a valid value to the parcel.
switch (this.type) {
@@ -1434,13 +1586,13 @@
out.writeDouble((Double) this.value);
break;
case CHAR:
- out.writeInt((int)((Character)this.value).charValue());
+ out.writeInt((int) ((Character) this.value).charValue());
break;
case STRING:
- out.writeString8((String)this.value);
+ out.writeString8((String) this.value);
break;
case CHAR_SEQUENCE:
- TextUtils.writeToParcel((CharSequence)this.value, out, flags);
+ TextUtils.writeToParcel((CharSequence) this.value, out, flags);
break;
case BUNDLE:
out.writeBundle((Bundle) this.value);
@@ -1457,135 +1609,134 @@
}
}
- private Class<?> getParameterType() {
- switch (this.type) {
- case BOOLEAN:
- return boolean.class;
- case BYTE:
- return byte.class;
- case SHORT:
- return short.class;
- case INT:
- return int.class;
- case LONG:
- return long.class;
- case FLOAT:
- return float.class;
- case DOUBLE:
- return double.class;
- case CHAR:
- return char.class;
- case STRING:
- return String.class;
- case CHAR_SEQUENCE:
- return CharSequence.class;
- case URI:
- return Uri.class;
- case BITMAP:
- return Bitmap.class;
- case BUNDLE:
- return Bundle.class;
- case INTENT:
- return Intent.class;
- case COLOR_STATE_LIST:
- return ColorStateList.class;
- case ICON:
- return Icon.class;
- default:
- return null;
- }
- }
-
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
- final View view = root.findViewById(viewId);
- if (view == null) return;
-
- Class<?> param = getParameterType();
- if (param == null) {
- throw new ActionException("bad type: " + this.type);
- }
- try {
- getMethod(view, this.methodName, param, false /* async */).invoke(view, this.value);
- } catch (Throwable ex) {
- throw new ActionException(ex);
- }
- }
-
- @Override
- public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
- final View view = root.findViewById(viewId);
- if (view == null) return ACTION_NOOP;
-
- Class<?> param = getParameterType();
- if (param == null) {
- throw new ActionException("bad type: " + this.type);
- }
-
- try {
- MethodHandle method = getMethod(view, this.methodName, param, true /* async */);
-
- if (method != null) {
- Runnable endAction = (Runnable) method.invoke(view, this.value);
- if (endAction == null) {
- return ACTION_NOOP;
- } else {
- // Special case view stub
- if (endAction instanceof ViewStub.ViewReplaceRunnable) {
- root.createTree();
- // Replace child tree
- root.findViewTreeById(viewId).replaceView(
- ((ViewStub.ViewReplaceRunnable) endAction).view);
- }
- return new RunnableAction(endAction);
- }
- }
- } catch (Throwable ex) {
- throw new ActionException(ex);
- }
-
- return this;
- }
-
- public int mergeBehavior() {
- // smoothScrollBy is cumulative, everything else overwites.
- if (methodName.equals("smoothScrollBy")) {
- return MERGE_APPEND;
- } else {
- return MERGE_REPLACE;
- }
+ protected Object getParameterValue(View view) throws ActionException {
+ return this.value;
}
@Override
public int getActionTag() {
return REFLECTION_ACTION_TAG;
}
+ }
- @Override
- public String getUniqueKey() {
- // Each type of reflection action corresponds to a setter, so each should be seen as
- // unique from the standpoint of merging.
- return super.getUniqueKey() + this.methodName + this.type;
+ private final class ResourceReflectionAction extends BaseReflectionAction {
+
+ static final int DIMEN_RESOURCE = 1;
+ static final int COLOR_RESOURCE = 2;
+ static final int STRING_RESOURCE = 3;
+
+ private final int mResourceType;
+ private final int mResId;
+
+ ResourceReflectionAction(@IdRes int viewId, String methodName, int parameterType,
+ int resourceType, int resId) {
+ super(viewId, methodName, parameterType);
+ this.mResourceType = resourceType;
+ this.mResId = resId;
+ }
+
+ ResourceReflectionAction(Parcel in) {
+ super(in);
+ this.mResourceType = in.readInt();
+ this.mResId = in.readInt();
}
@Override
- public boolean prefersAsyncApply() {
- return this.type == URI || this.type == ICON;
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(this.mResourceType);
+ dest.writeInt(this.mResId);
}
@Override
- public void visitUris(@NonNull Consumer<Uri> visitor) {
- switch (this.type) {
- case URI:
- final Uri uri = (Uri) this.value;
- visitor.accept(uri);
- break;
- case ICON:
- final Icon icon = (Icon) this.value;
- visitIconUri(icon, visitor);
- break;
+ protected @NonNull Object getParameterValue(View view) throws ActionException {
+ Resources resources = view.getContext().getResources();
+ try {
+ switch (this.mResourceType) {
+ case DIMEN_RESOURCE:
+ if (this.type == BaseReflectionAction.INT) {
+ return resources.getDimensionPixelSize(this.mResId);
+ }
+ return resources.getDimension(this.mResId);
+ case COLOR_RESOURCE:
+ switch(this.type) {
+ case BaseReflectionAction.INT:
+ return view.getContext().getColor(this.mResId);
+ case BaseReflectionAction.COLOR_STATE_LIST:
+ return view.getContext().getColorStateList(this.mResId);
+ default:
+ throw new ActionException(
+ "color resources must be used as int or ColorStateList, "
+ + "not " + this.type);
+ }
+ case STRING_RESOURCE:
+ return resources.getText(this.mResId);
+ default:
+ throw new ActionException("unknown resource type: " + this.mResourceType);
+ }
+ } catch (Throwable t) {
+ throw new ActionException(t);
}
}
+
+ @Override
+ public int getActionTag() {
+ return RESOURCE_REFLECTION_ACTION_TAG;
+ }
+ }
+
+ private final class ComplexUnitDimensionReflectionAction extends BaseReflectionAction {
+
+ private final float mValue;
+ @ComplexDimensionUnit
+ private final int mUnit;
+
+ ComplexUnitDimensionReflectionAction(int viewId, String methodName, int parameterType,
+ float value, @ComplexDimensionUnit int unit) {
+ super(viewId, methodName, parameterType);
+ this.mValue = value;
+ this.mUnit = unit;
+ }
+
+ ComplexUnitDimensionReflectionAction(Parcel in) {
+ super(in);
+ this.mValue = in.readFloat();
+ this.mUnit = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeFloat(this.mValue);
+ dest.writeInt(this.mUnit);
+ }
+
+ @Override
+ protected Object getParameterValue(View view) throws ActionException {
+ DisplayMetrics dm = view.getContext().getResources().getDisplayMetrics();
+ try {
+ int data = TypedValue.createComplexDimension(this.mValue, this.mUnit);
+ switch (this.type) {
+ case ReflectionAction.INT:
+ return TypedValue.complexToDimensionPixelSize(data, dm);
+ case ReflectionAction.FLOAT:
+ return TypedValue.complexToDimension(data, dm);
+ default:
+ throw new ActionException(
+ "parameter type must be INT or FLOAT, not " + this.type);
+ }
+ } catch (ActionException ex) {
+ throw ex;
+ } catch (Throwable t) {
+ throw new ActionException(t);
+ }
+ }
+
+ @Override
+ public int getActionTag() {
+ return COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG;
+ }
}
/**
@@ -2611,6 +2762,10 @@
return new SetIntTagAction(parcel);
case REMOVE_FROM_PARENT_ACTION_TAG:
return new RemoveFromParentAction(parcel);
+ case RESOURCE_REFLECTION_ACTION_TAG:
+ return new ResourceReflectionAction(parcel);
+ case COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG:
+ return new ComplexUnitDimensionReflectionAction(parcel);
default:
throw new ActionException("Tag " + tag + " not found");
}
@@ -3113,7 +3268,7 @@
*/
public void setProgressTintList(@IdRes int viewId, ColorStateList tint) {
addAction(new ReflectionAction(viewId, "setProgressTintList",
- ReflectionAction.COLOR_STATE_LIST, tint));
+ BaseReflectionAction.COLOR_STATE_LIST, tint));
}
/**
@@ -3125,7 +3280,7 @@
*/
public void setProgressBackgroundTintList(@IdRes int viewId, ColorStateList tint) {
addAction(new ReflectionAction(viewId, "setProgressBackgroundTintList",
- ReflectionAction.COLOR_STATE_LIST, tint));
+ BaseReflectionAction.COLOR_STATE_LIST, tint));
}
/**
@@ -3137,7 +3292,7 @@
*/
public void setProgressIndeterminateTintList(@IdRes int viewId, ColorStateList tint) {
addAction(new ReflectionAction(viewId, "setIndeterminateTintList",
- ReflectionAction.COLOR_STATE_LIST, tint));
+ BaseReflectionAction.COLOR_STATE_LIST, tint));
}
/**
@@ -3159,8 +3314,8 @@
* @param colors the text colors to set
*/
public void setTextColor(@IdRes int viewId, ColorStateList colors) {
- addAction(new ReflectionAction(viewId, "setTextColor", ReflectionAction.COLOR_STATE_LIST,
- colors));
+ addAction(new ReflectionAction(viewId, "setTextColor",
+ BaseReflectionAction.COLOR_STATE_LIST, colors));
}
/**
@@ -3353,7 +3508,7 @@
* @param value The value to pass to the method.
*/
public void setBoolean(@IdRes int viewId, String methodName, boolean value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BOOLEAN, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.BOOLEAN, value));
}
/**
@@ -3364,7 +3519,7 @@
* @param value The value to pass to the method.
*/
public void setByte(@IdRes int viewId, String methodName, byte value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BYTE, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.BYTE, value));
}
/**
@@ -3375,7 +3530,7 @@
* @param value The value to pass to the method.
*/
public void setShort(@IdRes int viewId, String methodName, short value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.SHORT, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.SHORT, value));
}
/**
@@ -3386,10 +3541,59 @@
* @param value The value to pass to the method.
*/
public void setInt(@IdRes int viewId, String methodName, int value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INT, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.INT, value));
}
/**
+ * Call a method taking one int, a size in pixels, on a view in the layout for this
+ * RemoteViews.
+ *
+ * The dimension will be resolved from the resources at the time of inflation.
+ *
+ * @param viewId The id of the view on which to call the method.
+ * @param methodName The name of the method to call.
+ * @param dimenResource The resource to resolve and pass as argument to the method.
+ */
+ public void setIntDimen(@IdRes int viewId, @NonNull String methodName,
+ @DimenRes int dimenResource) {
+ addAction(new ResourceReflectionAction(viewId, methodName, BaseReflectionAction.INT,
+ ResourceReflectionAction.DIMEN_RESOURCE, dimenResource));
+ }
+
+ /**
+ * Call a method taking one int, a size in pixels, on a view in the layout for this
+ * RemoteViews.
+ *
+ * The dimension will be resolved from the specified dimension at the time of inflation.
+ *
+ * @param viewId The id of the view on which to call the method.
+ * @param methodName The name of the method to call.
+ * @param value The value of the dimension.
+ * @param unit The unit in which the value is specified.
+ */
+ public void setIntDimen(@IdRes int viewId, @NonNull String methodName,
+ float value, @ComplexDimensionUnit int unit) {
+ addAction(new ComplexUnitDimensionReflectionAction(viewId, methodName, ReflectionAction.INT,
+ value, unit));
+ }
+
+ /**
+ * Call a method taking one int, a color, on a view in the layout for this RemoteViews.
+ *
+ * The ColorStateList will be resolved from the resources at the time of inflation.
+ *
+ * @param viewId The id of the view on which to call the method.
+ * @param methodName The name of the method to call.
+ * @param colorResource The resource to resolve and pass as argument to the method.
+ */
+ public void setColor(@IdRes int viewId, @NonNull String methodName,
+ @ColorRes int colorResource) {
+ addAction(new ResourceReflectionAction(viewId, methodName, BaseReflectionAction.INT,
+ ResourceReflectionAction.COLOR_RESOURCE, colorResource));
+ }
+
+
+ /**
* Call a method taking one ColorStateList on a view in the layout for this RemoteViews.
*
* @param viewId The id of the view on which to call the method.
@@ -3399,10 +3603,25 @@
* @hide
*/
public void setColorStateList(@IdRes int viewId, String methodName, ColorStateList value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.COLOR_STATE_LIST,
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.COLOR_STATE_LIST,
value));
}
+ /**
+ * Call a method taking one ColorStateList on a view in the layout for this RemoteViews.
+ *
+ * The ColorStateList will be resolved from the resources at the time of inflation.
+ *
+ * @param viewId The id of the view on which to call the method.
+ * @param methodName The name of the method to call.
+ * @param colorResource The resource to resolve and pass as argument to the method.
+ */
+ public void setColorStateList(@IdRes int viewId, @NonNull String methodName,
+ @ColorRes int colorResource) {
+ addAction(new ResourceReflectionAction(viewId, methodName,
+ BaseReflectionAction.COLOR_STATE_LIST, ResourceReflectionAction.COLOR_RESOURCE,
+ colorResource));
+ }
/**
* Call a method taking one long on a view in the layout for this RemoteViews.
@@ -3412,7 +3631,7 @@
* @param value The value to pass to the method.
*/
public void setLong(@IdRes int viewId, String methodName, long value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.LONG, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.LONG, value));
}
/**
@@ -3423,7 +3642,41 @@
* @param value The value to pass to the method.
*/
public void setFloat(@IdRes int viewId, String methodName, float value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.FLOAT, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.FLOAT, value));
+ }
+
+ /**
+ * Call a method taking one float, a size in pixels, on a view in the layout for this
+ * RemoteViews.
+ *
+ * The dimension will be resolved from the resources at the time of inflation.
+ *
+ * @param viewId The id of the view on which to call the method.
+ * @param methodName The name of the method to call.
+ * @param dimenResource The resource to resolve and pass as argument to the method.
+ */
+ public void setFloatDimen(@IdRes int viewId, @NonNull String methodName,
+ @DimenRes int dimenResource) {
+ addAction(new ResourceReflectionAction(viewId, methodName, BaseReflectionAction.FLOAT,
+ ResourceReflectionAction.DIMEN_RESOURCE, dimenResource));
+ }
+
+ /**
+ * Call a method taking one float, a size in pixels, on a view in the layout for this
+ * RemoteViews.
+ *
+ * The dimension will be resolved from the specified dimension at the time of inflation.
+ *
+ * @param viewId The id of the view on which to call the method.
+ * @param methodName The name of the method to call.
+ * @param value The value of the dimension.
+ * @param unit The unit in which the value is specified.
+ */
+ public void setFloatDimen(@IdRes int viewId, @NonNull String methodName,
+ float value, @ComplexDimensionUnit int unit) {
+ addAction(
+ new ComplexUnitDimensionReflectionAction(viewId, methodName, ReflectionAction.FLOAT,
+ value, unit));
}
/**
@@ -3434,7 +3687,7 @@
* @param value The value to pass to the method.
*/
public void setDouble(@IdRes int viewId, String methodName, double value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.DOUBLE, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.DOUBLE, value));
}
/**
@@ -3445,7 +3698,7 @@
* @param value The value to pass to the method.
*/
public void setChar(@IdRes int viewId, String methodName, char value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.CHAR, value));
}
/**
@@ -3456,7 +3709,7 @@
* @param value The value to pass to the method.
*/
public void setString(@IdRes int viewId, String methodName, String value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.STRING, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.STRING, value));
}
/**
@@ -3467,7 +3720,24 @@
* @param value The value to pass to the method.
*/
public void setCharSequence(@IdRes int viewId, String methodName, CharSequence value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.CHAR_SEQUENCE,
+ value));
+ }
+
+ /**
+ * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
+ *
+ * The CharSequence will be resolved from the resources at the time of inflation.
+ *
+ * @param viewId The id of the view on which to call the method.
+ * @param methodName The name of the method to call.
+ * @param stringResource The resource to resolve and pass as argument to the method.
+ */
+ public void setCharSequence(@IdRes int viewId, @NonNull String methodName,
+ @StringRes int stringResource) {
+ addAction(
+ new ResourceReflectionAction(viewId, methodName, BaseReflectionAction.CHAR_SEQUENCE,
+ ResourceReflectionAction.STRING_RESOURCE, stringResource));
}
/**
@@ -3485,7 +3755,7 @@
value.checkFileUriExposed("RemoteViews.setUri()");
}
}
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.URI, value));
}
/**
@@ -3510,7 +3780,7 @@
* @param value The value to pass to the method.
*/
public void setBundle(@IdRes int viewId, String methodName, Bundle value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BUNDLE, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.BUNDLE, value));
}
/**
@@ -3521,7 +3791,7 @@
* @param value The {@link android.content.Intent} to pass the method.
*/
public void setIntent(@IdRes int viewId, String methodName, Intent value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INTENT, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.INTENT, value));
}
/**
@@ -3532,7 +3802,7 @@
* @param value The {@link android.graphics.drawable.Icon} to pass the method.
*/
public void setIcon(@IdRes int viewId, String methodName, Icon value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.ICON, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.ICON, value));
}
/**
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/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index bad21d2..73c5460 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -148,6 +148,7 @@
private int mRegularColor;
private int mErrorColor;
private int mSuccessColor;
+ private int mDotColor;
private final Interpolator mFastOutSlowInInterpolator;
private final Interpolator mLinearOutSlowInInterpolator;
@@ -318,6 +319,7 @@
mRegularColor = a.getColor(R.styleable.LockPatternView_regularColor, 0);
mErrorColor = a.getColor(R.styleable.LockPatternView_errorColor, 0);
mSuccessColor = a.getColor(R.styleable.LockPatternView_successColor, 0);
+ mDotColor = a.getColor(R.styleable.LockPatternView_dotColor, mRegularColor);
int pathColor = a.getColor(R.styleable.LockPatternView_pathColor, mRegularColor);
mPathPaint.setColor(pathColor);
@@ -522,7 +524,7 @@
getCenterYForRow(cellState.row) + startTranslationY);
cellState.hwCenterX = CanvasProperty.createFloat(getCenterXForColumn(cellState.col));
cellState.hwRadius = CanvasProperty.createFloat(mDotSize/2 * startScale);
- mPaint.setColor(getCurrentColor(false));
+ mPaint.setColor(getDotColor());
mPaint.setAlpha((int) (startAlpha * 255));
cellState.hwPaint = CanvasProperty.createPaint(new Paint(mPaint));
@@ -1163,29 +1165,6 @@
final Path currentPath = mCurrentPath;
currentPath.rewind();
- // draw the circles
- for (int i = 0; i < 3; i++) {
- float centerY = getCenterYForRow(i);
- for (int j = 0; j < 3; j++) {
- CellState cellState = mCellStates[i][j];
- float centerX = getCenterXForColumn(j);
- float translationY = cellState.translationY;
-
- if (mUseLockPatternDrawable) {
- drawCellDrawable(canvas, i, j, cellState.radius, drawLookup[i][j]);
- } else {
- if (isHardwareAccelerated() && cellState.hwAnimating) {
- RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
- recordingCanvas.drawCircle(cellState.hwCenterX, cellState.hwCenterY,
- cellState.hwRadius, cellState.hwPaint);
- } else {
- drawCircle(canvas, (int) centerX, (int) centerY + translationY,
- cellState.radius, drawLookup[i][j], cellState.alpha);
- }
- }
- }
- }
-
// TODO: the path should be created and cached every time we hit-detect a cell
// only the last segment of the path should be computed here
// draw the path of the pattern (unless we are in stealth mode)
@@ -1256,6 +1235,29 @@
canvas.drawPath(currentPath, mPathPaint);
}
}
+
+ // draw the circles
+ for (int i = 0; i < 3; i++) {
+ float centerY = getCenterYForRow(i);
+ for (int j = 0; j < 3; j++) {
+ CellState cellState = mCellStates[i][j];
+ float centerX = getCenterXForColumn(j);
+ float translationY = cellState.translationY;
+
+ if (mUseLockPatternDrawable) {
+ drawCellDrawable(canvas, i, j, cellState.radius, drawLookup[i][j]);
+ } else {
+ if (isHardwareAccelerated() && cellState.hwAnimating) {
+ RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
+ recordingCanvas.drawCircle(cellState.hwCenterX, cellState.hwCenterY,
+ cellState.hwRadius, cellState.hwPaint);
+ } else {
+ drawCircle(canvas, (int) centerX, (int) centerY + translationY,
+ cellState.radius, drawLookup[i][j], cellState.alpha);
+ }
+ }
+ }
+ }
}
private float calculateLastSegmentAlpha(float x, float y, float lastX, float lastY) {
@@ -1266,6 +1268,17 @@
return Math.min(1f, Math.max(0f, (frac - 0.3f) * 4f));
}
+ private int getDotColor() {
+ if (mInStealthMode) {
+ // Always use the default color in this case
+ return mDotColor;
+ } else if (mPatternDisplayMode == DisplayMode.Wrong) {
+ // the pattern is wrong
+ return mErrorColor;
+ }
+ return mDotColor;
+ }
+
private int getCurrentColor(boolean partOfPattern) {
if (!partOfPattern || mInStealthMode || mPatternInProgress) {
// unselected circle
@@ -1286,7 +1299,7 @@
*/
private void drawCircle(Canvas canvas, float centerX, float centerY, float radius,
boolean partOfPattern, float alpha) {
- mPaint.setColor(getCurrentColor(partOfPattern));
+ mPaint.setColor(getDotColor());
mPaint.setAlpha((int) (alpha * 255));
canvas.drawCircle(centerX, centerY, radius, mPaint);
}
diff --git a/core/java/com/android/internal/widget/OWNERS b/core/java/com/android/internal/widget/OWNERS
index ae566c3..3fc3933 100644
--- a/core/java/com/android/internal/widget/OWNERS
+++ b/core/java/com/android/internal/widget/OWNERS
@@ -5,3 +5,16 @@
per-file *LockScreen* = file:/services/core/java/com/android/server/locksettings/OWNERS
per-file *Lockscreen* = file:/services/core/java/com/android/server/locksettings/OWNERS
per-file *LockSettings* = file:/services/core/java/com/android/server/locksettings/OWNERS
+
+# Notification related
+per-file *Notification* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *Messaging* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *Message* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *Conversation* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *ImageResolver* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file CallLayout.java = file:/services/core/java/com/android/server/notification/OWNERS
+per-file CachingIconView.java = file:/services/core/java/com/android/server/notification/OWNERS
+per-file ImageFloatingTextView.java = file:/services/core/java/com/android/server/notification/OWNERS
+per-file ObservableTextView.java = file:/services/core/java/com/android/server/notification/OWNERS
+per-file RemeasuringLinearLayout.java = file:/services/core/java/com/android/server/notification/OWNERS
+per-file ViewClippingUtil.java = file:/services/core/java/com/android/server/notification/OWNERS
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index f5aa17d..632d372 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -518,6 +518,11 @@
}
optional Search search = 48;
+ message CameraAutorotate {
+ optional SettingProto enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+ optional CameraAutorotate camera_autorotate = 88;
+
message SpellChecker {
option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -642,5 +647,5 @@
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 88;
+ // Next tag = 89;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 08a97b8..989e25d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1748,7 +1748,7 @@
@SystemApi
<p>Not for use by third-party applications. @hide -->
<permission android:name="android.permission.RESTART_WIFI_SUBSYSTEM"
- android:protectionLevel="signature|setup" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide Allows applications to toggle airplane mode.
<p>Not for use by third-party or privileged applications.
@@ -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
@@ -5298,7 +5293,8 @@
android:protectionLevel="signature" />
<!-- Allows financial apps to read filtered sms messages.
- Protection level: signature|appop -->
+ Protection level: signature|appop
+ @deprecated The API that used this permission is no longer functional. -->
<permission android:name="android.permission.SMS_FINANCIAL_TRANSACTIONS"
android:protectionLevel="signature|appop" />
diff --git a/core/res/OWNERS b/core/res/OWNERS
index a30111b..9d739b9 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -6,9 +6,12 @@
dupin@google.com
hackbod@android.com
hackbod@google.com
+ilyamaty@google.com
+jaggies@google.com
jsharkey@android.com
jsharkey@google.com
juliacr@google.com
+kchyn@google.com
michaelwr@google.com
nandana@google.com
narayan@google.com
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 07c3adf..39e60d0 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}.
@@ -8436,6 +8428,9 @@
<attr name="errorColor" format="color|reference" />
<!-- The success color -->
<attr name="successColor" format="color|reference"/>
+ <!-- The dot color -->
+ <attr name="dotColor" format="color|reference"/>
+
</declare-styleable>
<!-- =============================== -->
@@ -9194,6 +9189,9 @@
<!-- @hide From Theme.colorBackground, used for the TaskDescription background
color. -->
<attr name="colorBackground" />
+ <!-- @hide From Theme.colorBackgroundFloating, used for the TaskDescription background
+ color floating. -->
+ <attr name="colorBackgroundFloating" />
<!-- @hide From Theme.statusBarColor, used for the TaskDescription status bar color. -->
<attr name="statusBarColor"/>
<!-- @hide From Theme.navigationBarColor, used for the TaskDescription navigation bar
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 01b8efa..416bc84 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -517,6 +517,9 @@
<!-- Flag indicating whether we should enable smart battery. -->
<bool name="config_smart_battery_available">false</bool>
+ <!-- Flag indicating whether we should enable camera-based autorotate -->
+ <bool name="config_camera_autorotate">false</bool>
+
<!-- Fast brightness animation ramp rate in brightness units per second-->
<integer translatable="false" name="config_brightness_ramp_rate_fast">180</integer>
@@ -4404,6 +4407,15 @@
<!--If true, allows the device to load udfps components on older HIDL implementations -->
<bool name="allow_test_udfps" translatable="false" >false</bool>
+ <!-- The properties of a UDFPS sensor in pixels, in the order listed below: -->
+ <integer-array name="config_udfps_sensor_props" translatable="false" >
+ <!--
+ <item>sensorLocationX</item>
+ <item>sensorLocationY</item>
+ <item>sensorRadius</item>
+ -->
+ </integer-array>
+
<!-- Messages that should not be shown to the user during face auth enrollment. This should be
used to hide messages that may be too chatty or messages that the user can't do much about.
Entries are defined in android.hardware.biometrics.face@1.0 types.hal -->
@@ -4645,6 +4657,23 @@
corners of the activity won't be rounded. -->
<integer name="config_letterboxActivityCornersRadius">0</integer>
+ <!-- Corners appearance of the letterbox background.
+ 0 - Solid background using color specified in R.color.config_letterboxBackgroundColor.
+ 1 - Color specified in R.attr.colorBackground for the letterboxed application.
+ 2 - Color specified in R.attr.colorBackgroundFloating for the letterboxed application.
+ If given value is outside of this range, the option 0 will be assummed. -->
+ <integer name="config_letterboxBackgroundType">0</integer>
+
+ <!-- Color of the letterbox background if one following conditions is true
+ - Option 0 is selected for R.integer.config_letterboxBackgroundType.
+ - Option 1 is selected for R.integer.config_letterboxBackgroundType and
+ R.attr.colorBackground isn't specified for the app.
+ - Option 2 is selected for R.integer.config_letterboxBackgroundType and
+ R.attr.colorBackgroundFloating isn't specified for the app.
+ Defaults to black if not specified.
+ -->
+ <color name="config_letterboxBackgroundColor">#000</color>
+
<!-- If true, hide the display cutout with display area -->
<bool name="config_hideDisplayCutoutWithDisplayArea">false</bool>
@@ -4679,4 +4708,7 @@
<!-- Whether to select voice/data/sms preference without user confirmation -->
<bool name="config_voice_data_sms_auto_fallback">false</bool>
+
+ <!-- Whether to enable the one-handed keyguard on the lock screen for wide-screen devices. -->
+ <bool name="config_enableOneHandedKeyguard">false</bool>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 3ae2131..cb16af5 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -622,9 +622,9 @@
aliasing effects). This is only used on circular displays. -->
<dimen name="circular_display_mask_thickness">1px</dimen>
- <dimen name="lock_pattern_dot_line_width">3dp</dimen>
- <dimen name="lock_pattern_dot_size">12dp</dimen>
- <dimen name="lock_pattern_dot_size_activated">28dp</dimen>
+ <dimen name="lock_pattern_dot_line_width">22dp</dimen>
+ <dimen name="lock_pattern_dot_size">14dp</dimen>
+ <dimen name="lock_pattern_dot_size_activated">30dp</dimen>
<dimen name="text_handle_min_size">40dp</dimen>
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/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 815330f..672aec6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1881,6 +1881,7 @@
<java-symbol type="bool" name="config_enableServerNotificationEffectsForAutomotive" />
<java-symbol type="bool" name="config_useAttentionLight" />
<java-symbol type="bool" name="config_adaptive_sleep_available" />
+ <java-symbol type="bool" name="config_camera_autorotate"/>
<java-symbol type="bool" name="config_animateScreenLights" />
<java-symbol type="bool" name="config_automatic_brightness_available" />
<java-symbol type="bool" name="config_smart_battery_available" />
@@ -2540,6 +2541,7 @@
<java-symbol type="string" name="config_biometric_prompt_ui_package" />
<java-symbol type="array" name="config_biometric_sensors" />
<java-symbol type="bool" name="allow_test_udfps" />
+ <java-symbol type="array" name="config_udfps_sensor_props" />
<java-symbol type="array" name="config_face_acquire_enroll_ignorelist" />
<java-symbol type="array" name="config_face_acquire_vendor_enroll_ignorelist" />
@@ -4134,6 +4136,8 @@
<java-symbol type="dimen" name="config_taskLetterboxAspectRatio" />
<java-symbol type="integer" name="config_letterboxActivityCornersRadius" />
+ <java-symbol type="integer" name="config_letterboxBackgroundType" />
+ <java-symbol type="color" name="config_letterboxBackgroundColor" />
<java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" />
@@ -4170,4 +4174,6 @@
<java-symbol type="bool" name="config_telephony5gNonStandalone" />
<java-symbol type="bool" name="config_voice_data_sms_auto_fallback" />
+
+ <java-symbol type="bool" name="config_enableOneHandedKeyguard" />
</resources>
diff --git a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
index 0c351d1..81eb213 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
@@ -132,7 +132,8 @@
true, // ensureNavigationBarContrastWhenTransparent
RESIZE_MODE_RESIZEABLE, // resizeMode
10, // minWidth
- 20 // minHeight
+ 20, // minHeight
+ 0 // colorBackgroundFloating
);
TaskDescription td2 = new TaskDescription();
@@ -155,7 +156,8 @@
false, // ensureNavigationBarContrastWhenTransparent
RESIZE_MODE_UNRESIZEABLE, // resizeMode
10, // minWidth
- 20 // minHeight
+ 20, // minHeight
+ 0 // colorBackgroundFloating
);
TaskDescription td2 = new TaskDescription(
@@ -169,7 +171,8 @@
true, // ensureNavigationBarContrastWhenTransparent
RESIZE_MODE_RESIZEABLE, // resizeMode
102, // minWidth
- 202 // minHeight
+ 202, // minHeight
+ 0 // colorBackgroundFloating
);
// Must overwrite all public and hidden fields, since other has all fields set.
@@ -198,7 +201,8 @@
false, // ensureNavigationBarContrastWhenTransparent
RESIZE_MODE_UNRESIZEABLE, // resizeMode
10, // minWidth
- 20 // minHeight
+ 20, // minHeight
+ 0 // colorBackgroundFloating
);
// Normal parceling should keep everything the same.
@@ -219,7 +223,8 @@
false, // ensureNavigationBarContrastWhenTransparent
RESIZE_MODE_UNRESIZEABLE, // resizeMode
10, // minWidth
- 20 // minHeight
+ 20, // minHeight
+ 0 // colorBackgroundFloating
);
// Recycled bitmap will be ignored while parceling.
tdParcelled = new TaskDescription(parcelingRoundTrip(tdBitmapRecycled));
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/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 9705284..cfe3803 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -27,6 +27,10 @@
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
+import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
+import static android.view.RoundedCorner.POSITION_TOP_LEFT;
+import static android.view.RoundedCorner.POSITION_TOP_RIGHT;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
@@ -427,6 +431,27 @@
cutout.getBoundingRectBottom());
}
+ @Test
+ public void testCalculateRelativeRoundedCorners() {
+ mState.setDisplayFrame(new Rect(0, 0, 200, 400));
+ mState.setRoundedCorners(new RoundedCorners(
+ new RoundedCorner(POSITION_TOP_LEFT, 10, 10, 10),
+ new RoundedCorner(POSITION_TOP_RIGHT, 10, 190, 10),
+ new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 180, 380),
+ new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 20, 380)));
+ WindowInsets windowInsets = mState.calculateInsets(new Rect(1, 2, 197, 396), null, false,
+ false, SOFT_INPUT_ADJUST_UNSPECIFIED, 0, 0, TYPE_APPLICATION,
+ WINDOWING_MODE_UNDEFINED, new SparseIntArray());
+ assertEquals(new RoundedCorner(POSITION_TOP_LEFT, 10, 9, 8),
+ windowInsets.getRoundedCorner(POSITION_TOP_LEFT));
+ assertEquals(new RoundedCorner(POSITION_TOP_RIGHT, 10, 189, 8),
+ windowInsets.getRoundedCorner(POSITION_TOP_RIGHT));
+ assertEquals(new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 179, 378),
+ windowInsets.getRoundedCorner(POSITION_BOTTOM_RIGHT));
+ assertEquals(new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 19, 378),
+ windowInsets.getRoundedCorner(POSITION_BOTTOM_LEFT));
+ }
+
private void assertEqualsAndHashCode() {
assertEquals(mState, mState2);
assertEquals(mState.hashCode(), mState2.hashCode());
diff --git a/core/tests/coretests/src/android/view/RoundedCornerTest.java b/core/tests/coretests/src/android/view/RoundedCornerTest.java
new file mode 100644
index 0000000..8eb13bc
--- /dev/null
+++ b/core/tests/coretests/src/android/view/RoundedCornerTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import android.graphics.Point;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class RoundedCornerTest {
+
+ @Test
+ public void testGetPosition() {
+ RoundedCorner roundedCorner = new RoundedCorner(
+ RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
+ assertThat(roundedCorner.getPosition(), is(RoundedCorner.POSITION_BOTTOM_LEFT));
+ }
+
+ @Test
+ public void testGetRadius() {
+ RoundedCorner roundedCorner = new RoundedCorner(
+ RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
+ assertThat(roundedCorner.getRadius(), is(2));
+ }
+
+ @Test
+ public void testGetCenter() {
+ RoundedCorner roundedCorner = new RoundedCorner(
+ RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
+ assertThat(roundedCorner.getCenter(), equalTo(new Point(3, 4)));
+ }
+
+ @Test
+ public void testIsEmpty() {
+ RoundedCorner roundedCorner = new RoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
+ assertThat(roundedCorner.isEmpty(), is(true));
+ }
+
+ @Test
+ public void testEquals() {
+ RoundedCorner roundedCorner = new RoundedCorner(
+ RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
+ RoundedCorner roundedCorner2 = new RoundedCorner(
+ RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
+ assertThat(roundedCorner, equalTo(roundedCorner2));
+ }
+}
diff --git a/core/tests/coretests/src/android/view/RoundedCornersTest.java b/core/tests/coretests/src/android/view/RoundedCornersTest.java
new file mode 100644
index 0000000..07ef33a
--- /dev/null
+++ b/core/tests/coretests/src/android/view/RoundedCornersTest.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
+import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
+import static android.view.RoundedCorner.POSITION_TOP_LEFT;
+import static android.view.RoundedCorner.POSITION_TOP_RIGHT;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+import android.util.Pair;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class RoundedCornersTest {
+
+ final RoundedCorners mRoundedCorners = new RoundedCorners(
+ new RoundedCorner(POSITION_TOP_LEFT, 10, 10, 10),
+ new RoundedCorner(POSITION_TOP_RIGHT, 10, 190, 10),
+ new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 180, 380),
+ new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 20, 380));
+
+ @Test
+ public void testGetRoundedCorner() {
+ final Pair<Integer, Integer> radius = new Pair<>(10, 20);
+ RoundedCorners roundedCorners = RoundedCorners.fromRadii(radius, 200, 400);
+
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_LEFT),
+ equalTo(new RoundedCorner(POSITION_TOP_LEFT, 10, 10, 10)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_RIGHT),
+ equalTo(new RoundedCorner(POSITION_TOP_RIGHT, 10, 190, 10)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_RIGHT),
+ equalTo(new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 180, 380)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT),
+ equalTo(new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 20, 380)));
+ }
+
+ @Test
+ public void testGetRoundedCorner_noRoundedCorners() {
+ RoundedCorners roundedCorners = RoundedCorners.NO_ROUNDED_CORNERS;
+
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_LEFT), nullValue());
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_RIGHT), nullValue());
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_RIGHT), nullValue());
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT), nullValue());
+ }
+
+ @Test
+ public void testHashCode() {
+ assertThat(mRoundedCorners.hashCode(),
+ equalTo(RoundedCorners.fromRadii(new Pair<>(10, 20), 200, 400).hashCode()));
+ assertThat(mRoundedCorners.hashCode(),
+ not(equalTo(RoundedCorners.fromRadii(new Pair<>(5, 10), 200, 400).hashCode())));
+ }
+
+ @Test
+ public void testEquals() {
+ assertThat(mRoundedCorners,
+ equalTo(RoundedCorners.fromRadii(new Pair<>(10, 20), 200, 400)));
+
+ assertThat(mRoundedCorners,
+ not(equalTo(RoundedCorners.fromRadii(new Pair<>(5, 10), 200, 400))));
+ }
+
+ @Test
+ public void testSetRoundedCorner() {
+ RoundedCorner roundedCorner = new RoundedCorner(POSITION_BOTTOM_LEFT, 5, 6, 7);
+ mRoundedCorners.setRoundedCorner(POSITION_BOTTOM_LEFT, roundedCorner);
+
+ assertThat(mRoundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT), equalTo(roundedCorner));
+ }
+
+ @Test
+ public void testSetRoundedCorner_null() {
+ mRoundedCorners.setRoundedCorner(POSITION_BOTTOM_LEFT, null);
+
+ assertThat(mRoundedCorners.mRoundedCorners[POSITION_BOTTOM_LEFT],
+ equalTo(new RoundedCorner(POSITION_BOTTOM_LEFT)));
+ assertThat(mRoundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT), nullValue());
+ }
+
+ @Test
+ public void testInsetRoundedCorners_partialOverlap() {
+ RoundedCorners roundedCorners = mRoundedCorners.inset(1, 2, 3, 4);
+
+ assertThat(roundedCorners.mRoundedCorners[POSITION_TOP_LEFT],
+ equalTo(new RoundedCorner(POSITION_TOP_LEFT, 10, 9, 8)));
+ assertThat(roundedCorners.mRoundedCorners[POSITION_TOP_RIGHT],
+ equalTo(new RoundedCorner(POSITION_TOP_RIGHT, 10, 189, 8)));
+ assertThat(roundedCorners.mRoundedCorners[POSITION_BOTTOM_RIGHT],
+ equalTo(new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 179, 378)));
+ assertThat(roundedCorners.mRoundedCorners[POSITION_BOTTOM_LEFT],
+ equalTo(new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 19, 378)));
+ }
+
+ @Test
+ public void testInsetRoundedCorners_noOverlap() {
+ RoundedCorners roundedCorners = mRoundedCorners.inset(20, 20, 20, 20);
+
+ assertThat(roundedCorners.mRoundedCorners[POSITION_TOP_LEFT].isEmpty(), is(true));
+ assertThat(roundedCorners.mRoundedCorners[POSITION_TOP_RIGHT].isEmpty(), is(true));
+ assertThat(roundedCorners.mRoundedCorners[POSITION_BOTTOM_RIGHT].isEmpty(), is(true));
+ assertThat(roundedCorners.mRoundedCorners[POSITION_BOTTOM_LEFT].isEmpty(), is(true));
+ }
+
+ @Test
+ public void testRotateRoundedCorners_90() {
+ final Pair<Integer, Integer> radius = new Pair<>(10, 20);
+ RoundedCorners roundedCorners = RoundedCorners.fromRadii(radius, 200, 400)
+ .rotate(ROTATION_90, 200, 400);
+
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_LEFT),
+ equalTo(new RoundedCorner(POSITION_TOP_LEFT, 10, 10, 10)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_RIGHT),
+ equalTo(new RoundedCorner(POSITION_TOP_RIGHT, 20, 380, 20)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_RIGHT),
+ equalTo(new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 380, 180)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT),
+ equalTo(new RoundedCorner(POSITION_BOTTOM_LEFT, 10, 10, 190)));
+ }
+
+ @Test
+ public void testRotateRoundedCorners_270() {
+ final Pair<Integer, Integer> radius = new Pair<>(10, 20);
+ RoundedCorners roundedCorners = RoundedCorners.fromRadii(radius, 200, 400)
+ .rotate(ROTATION_270, 200, 400);
+
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_LEFT),
+ equalTo(new RoundedCorner(POSITION_TOP_LEFT, 20, 20, 20)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_RIGHT),
+ equalTo(new RoundedCorner(POSITION_TOP_RIGHT, 10, 390, 10)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_RIGHT),
+ equalTo(new RoundedCorner(POSITION_BOTTOM_RIGHT, 10, 390, 190)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT),
+ equalTo(new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 20, 180)));
+ }
+
+ @Test
+ public void testFromRadius_cache() {
+ final Pair<Integer, Integer> radius = new Pair<>(10, 20);
+ RoundedCorners cached = RoundedCorners.fromRadii(radius, 200, 400);
+
+ assertThat(RoundedCorners.fromRadii(radius, 200, 400), sameInstance(cached));
+ }
+
+ @Test
+ public void testFromRadius_wontCacheIfRadiusChanged() {
+ final Pair<Integer, Integer> radius = new Pair<>(10, 20);
+ RoundedCorners cached = RoundedCorners.fromRadii(radius, 200, 400);
+
+ assertThat(RoundedCorners.fromRadii(new Pair<>(5, 10), 200, 400),
+ not(sameInstance(cached)));
+ }
+
+ @Test
+ public void testFromRadius_wontCacheIfDisplayWidthChanged() {
+ final Pair<Integer, Integer> radius = new Pair<>(10, 20);
+ RoundedCorners cached = RoundedCorners.fromRadii(radius, 200, 400);
+
+ assertThat(RoundedCorners.fromRadii(radius, 100, 400),
+ not(sameInstance(cached)));
+ }
+
+ @Test
+ public void testFromRadius_wontCacheIfDisplayHeightChanged() {
+ final Pair<Integer, Integer> radius = new Pair<>(10, 20);
+ RoundedCorners cached = RoundedCorners.fromRadii(radius, 200, 400);
+
+ assertThat(RoundedCorners.fromRadii(radius, 200, 300),
+ not(sameInstance(cached)));
+ }
+}
diff --git a/core/tests/coretests/src/android/view/WindowInsetsTest.java b/core/tests/coretests/src/android/view/WindowInsetsTest.java
index 8e4e735..b80837f 100644
--- a/core/tests/coretests/src/android/view/WindowInsetsTest.java
+++ b/core/tests/coretests/src/android/view/WindowInsetsTest.java
@@ -61,7 +61,7 @@
WindowInsets.assignCompatInsets(maxInsets, new Rect(0, 10, 0, 0));
WindowInsets.assignCompatInsets(insets, new Rect(0, 0, 0, 0));
WindowInsets windowInsets = new WindowInsets(insets, maxInsets, visible, false, false, null,
- systemBars(), true /* compatIgnoreVisibility */);
+ null, systemBars(), true /* compatIgnoreVisibility */);
assertEquals(Insets.of(0, 10, 0, 0), windowInsets.getSystemWindowInsets());
}
}
diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTests.java b/core/tests/mockingcoretests/src/android/view/DisplayTests.java
new file mode 100644
index 0000000..5a3ea35
--- /dev/null
+++ b/core/tests/mockingcoretests/src/android/view/DisplayTests.java
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_90;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManagerGlobal;
+import android.util.DisplayMetrics;
+import android.view.DisplayAdjustments.FixedRotationAdjustments;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.quality.Strictness;
+
+import java.util.function.Consumer;
+
+/**
+ * Tests for {@link Display}.
+ *
+ * <p>Build/Install/Run:
+ *
+ * atest FrameworksMockingCoreTests:android.view.DisplayTests
+ */
+@RunWith(AndroidJUnit4.class)
+public class DisplayTests {
+
+ private static final int APP_WIDTH = 272;
+ private static final int APP_HEIGHT = 700;
+ // Tablet size device, ROTATION_0 corresponds to portrait.
+ private static final int LOGICAL_WIDTH = 700;
+ private static final int LOGICAL_HEIGHT = 1800;
+
+ // Bounds of the app when the device is in portrait mode.
+ private static Rect sAppBoundsPortrait = buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT);
+ private static Rect sAppBoundsLandscape = buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH);
+
+ private StaticMockitoSession mMockitoSession;
+
+ private DisplayManagerGlobal mDisplayManagerGlobal;
+ private Context mApplicationContext;
+ private DisplayInfo mDisplayInfo = new DisplayInfo();
+
+ @Before
+ public void setupTests() {
+ mMockitoSession = mockitoSession()
+ .mockStatic(DisplayManagerGlobal.class)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+
+ // Ensure no adjustments are set before each test.
+ mApplicationContext = ApplicationProvider.getApplicationContext();
+ DisplayAdjustments displayAdjustments =
+ mApplicationContext.getResources().getDisplayAdjustments();
+ displayAdjustments.setFixedRotationAdjustments(null);
+ mApplicationContext.getResources().overrideDisplayAdjustments(null);
+ mApplicationContext.getResources().getConfiguration().windowConfiguration.setAppBounds(
+ null);
+ mApplicationContext.getResources().getConfiguration().windowConfiguration.setMaxBounds(
+ null);
+ mDisplayInfo.rotation = ROTATION_0;
+
+ mDisplayManagerGlobal = mock(DisplayManagerGlobal.class);
+ doReturn(mDisplayInfo).when(mDisplayManagerGlobal).getDisplayInfo(anyInt());
+ }
+
+ @After
+ public void teardownTests() {
+ if (mMockitoSession != null) {
+ mMockitoSession.finishMocking();
+ }
+ Mockito.framework().clearInlineMocks();
+ }
+
+ @Test
+ public void testConstructor_defaultDisplayAdjustments_matchesDisplayInfo() {
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ assertThat(display.getDisplayAdjustments()).isEqualTo(
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ DisplayInfo actualDisplayInfo = new DisplayInfo();
+ display.getDisplayInfo(actualDisplayInfo);
+ verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
+ }
+
+ @Test
+ public void testConstructor_defaultResources_matchesDisplayInfo() {
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ assertThat(display.getDisplayAdjustments()).isEqualTo(
+ mApplicationContext.getResources().getDisplayAdjustments());
+ DisplayInfo actualDisplayInfo = new DisplayInfo();
+ display.getDisplayInfo(actualDisplayInfo);
+ verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
+ }
+
+ @Test
+ public void testGetRotation_defaultDisplayAdjustments_rotationNotAdjusted() {
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ assertThat(display.getRotation()).isEqualTo(ROTATION_0);
+ }
+
+ @Test
+ public void testGetRotation_displayAdjustmentsWithoutOverride_rotationNotAdjusted() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, but no override is set.
+ DisplayAdjustments displayAdjustments = DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+ final FixedRotationAdjustments fixedRotationAdjustments =
+ new FixedRotationAdjustments(ROTATION_90, APP_WIDTH, APP_HEIGHT,
+ DisplayCutout.NO_CUTOUT);
+ displayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
+ // GIVEN display is constructed with display adjustments.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ displayAdjustments);
+ // THEN rotation is not adjusted since no override was set.
+ assertThat(display.getRotation()).isEqualTo(ROTATION_0);
+ }
+
+ @Test
+ public void testGetRotation_resourcesWithoutOverride_rotationNotAdjusted() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, but no override is set.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN rotation is not adjusted since no override is set.
+ assertThat(display.getRotation()).isEqualTo(ROTATION_0);
+ }
+
+ @Test
+ public void testGetRotation_resourcesWithOverrideDisplayAdjustments_rotationAdjusted() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, and an override is set.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN rotation is adjusted since an override is set.
+ assertThat(display.getRotation()).isEqualTo(ROTATION_90);
+ }
+
+ @Test
+ public void testGetRealSize_defaultResourcesPortrait_matchesLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches display orientation.
+ verifyRealSizeIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealSize_defaultResourcesLandscape_matchesRotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches display orientation.
+ verifyRealSizeIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealSize_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ // THEN real size matches display orientation.
+ verifyRealSizeIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealSize_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ // THEN real size matches display orientation.
+ verifyRealSizeIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches display orientation.
+ verifyRealSizeIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches display orientation.
+ verifyRealSizeIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, and an override is set.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches app orientation.
+ verifyRealSizeIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, and an override is set.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches app orientation.
+ verifyRealSizeIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesPortraitSandboxed_matchesSandboxBounds() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN app is letterboxed.
+ setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
+ sAppBoundsPortrait);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches app bounds.
+ verifyRealSizeMatchesApp(display, sAppBoundsPortrait);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesLandscapeSandboxed_matchesSandboxBounds() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN app is letterboxed.
+ setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
+ sAppBoundsLandscape);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches app bounds.
+ verifyRealSizeMatchesApp(display, sAppBoundsLandscape);
+ }
+
+ @Test
+ public void testGetRealMetrics_defaultResourcesPortrait_matchesLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_defaultResourcesLandscape_matchesRotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated with an override.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches app orientation.
+ verifyRealMetricsIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches app orientation.
+ verifyRealMetricsIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesPortraitSandboxed_matchesSandboxBounds() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN app is letterboxed.
+ setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
+ sAppBoundsPortrait);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches app bounds.
+ verifyRealMetricsMatchesApp(display, sAppBoundsPortrait);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesSandboxBounds() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN app is letterboxed.
+ setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
+ sAppBoundsLandscape);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches app bounds.
+ verifyRealMetricsMatchesApp(display, sAppBoundsLandscape);
+ }
+
+ // Given rotated display dimensions, calculate the letterboxed app bounds.
+ private static Rect buildAppBounds(int displayWidth, int displayHeight) {
+ final int midWidth = displayWidth / 2;
+ final int left = midWidth - (APP_WIDTH / 2);
+ final int right = midWidth + (APP_WIDTH / 2);
+ final int midHeight = displayHeight / 2;
+ // Coordinate system starts at top left.
+ final int top = midHeight - (APP_HEIGHT / 2);
+ final int bottom = midHeight + (APP_HEIGHT / 2);
+ return new Rect(left, top, right, bottom);
+ }
+
+ private static void setDisplayInfoLandscape(DisplayInfo displayInfo) {
+ displayInfo.rotation = ROTATION_90;
+ // Flip width & height assignment since the device is rotated.
+ displayInfo.logicalWidth = LOGICAL_HEIGHT;
+ displayInfo.logicalHeight = LOGICAL_WIDTH;
+ }
+
+ private static void setDisplayInfoPortrait(DisplayInfo displayInfo) {
+ displayInfo.rotation = ROTATION_0;
+ displayInfo.logicalWidth = LOGICAL_WIDTH;
+ displayInfo.logicalHeight = LOGICAL_HEIGHT;
+ }
+
+ /**
+ * Set max bounds to be sandboxed to the app bounds, indicating the app is in
+ * size compat mode or letterbox.
+ */
+ private static void setMaxBoundsSandboxedToMatchAppBounds(Resources resources, Rect appBounds) {
+ resources.getConfiguration().windowConfiguration.setMaxBounds(appBounds);
+ }
+
+ /**
+ * Do not compare entire display info, since it is updated to match display the test is run on.
+ */
+ private static void verifyDisplayInfo(DisplayInfo actual, DisplayInfo expected) {
+ assertThat(actual.displayId).isEqualTo(expected.displayId);
+ assertThat(actual.rotation).isEqualTo(expected.rotation);
+ assertThat(actual.logicalWidth).isEqualTo(LOGICAL_WIDTH);
+ assertThat(actual.logicalHeight).isEqualTo(LOGICAL_HEIGHT);
+ }
+
+ private static void verifyRealSizeIsLandscape(Display display) {
+ Point size = new Point();
+ display.getRealSize(size);
+ // Flip the width and height check since the device is rotated.
+ assertThat(size).isEqualTo(new Point(LOGICAL_HEIGHT, LOGICAL_WIDTH));
+ }
+
+ private static void verifyRealMetricsIsLandscape(Display display) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getRealMetrics(metrics);
+ // Flip the width and height check since the device is rotated.
+ assertThat(metrics.widthPixels).isEqualTo(LOGICAL_HEIGHT);
+ assertThat(metrics.heightPixels).isEqualTo(LOGICAL_WIDTH);
+ }
+
+ private static void verifyRealSizeIsPortrait(Display display) {
+ Point size = new Point();
+ display.getRealSize(size);
+ assertThat(size).isEqualTo(new Point(LOGICAL_WIDTH, LOGICAL_HEIGHT));
+ }
+
+ private static void verifyRealMetricsIsPortrait(Display display) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getRealMetrics(metrics);
+ assertThat(metrics.widthPixels).isEqualTo(LOGICAL_WIDTH);
+ assertThat(metrics.heightPixels).isEqualTo(LOGICAL_HEIGHT);
+ }
+
+ private static void verifyRealSizeMatchesApp(Display display, Rect appBounds) {
+ Point size = new Point();
+ display.getRealSize(size);
+ assertThat(size).isEqualTo(new Point(appBounds.width(), appBounds.height()));
+ }
+
+ private static void verifyRealMetricsMatchesApp(Display display, Rect appBounds) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getRealMetrics(metrics);
+ assertThat(metrics.widthPixels).isEqualTo(appBounds.width());
+ assertThat(metrics.heightPixels).isEqualTo(appBounds.height());
+ }
+
+ private static FixedRotationAdjustments setOverrideFixedRotationAdjustments(
+ Resources resources, @Surface.Rotation int rotation) {
+ FixedRotationAdjustments fixedRotationAdjustments =
+ setFixedRotationAdjustments(resources, rotation);
+ resources.overrideDisplayAdjustments(
+ buildOverrideRotationAdjustments(fixedRotationAdjustments));
+ return fixedRotationAdjustments;
+ }
+
+ private static FixedRotationAdjustments setFixedRotationAdjustments(Resources resources,
+ @Surface.Rotation int rotation) {
+ final FixedRotationAdjustments fixedRotationAdjustments =
+ new FixedRotationAdjustments(rotation, APP_WIDTH, APP_HEIGHT,
+ DisplayCutout.NO_CUTOUT);
+ resources.getDisplayAdjustments().setFixedRotationAdjustments(fixedRotationAdjustments);
+ return fixedRotationAdjustments;
+ }
+
+ private static Consumer<DisplayAdjustments> buildOverrideRotationAdjustments(
+ FixedRotationAdjustments fixedRotationAdjustments) {
+ return consumedDisplayAdjustments
+ -> consumedDisplayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
+ }
+}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 301b491..b0ae9b9 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -223,14 +223,6 @@
targetSdk="29">
<new-permission name="android.permission.ACCESS_MEDIA_LOCATION" />
</split-permission>
- <split-permission name="android.permission.RECORD_AUDIO"
- targetSdk="31">
- <new-permission name="android.permission.RECORD_BACKGROUND_AUDIO" />
- </split-permission>
- <split-permission name="android.permission.CAMERA"
- targetSdk="31">
- <new-permission name="android.permission.BACKGROUND_CAMERA" />
- </split-permission>
<!-- This is a list of all the libraries available for application
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 222c9bd..ac8a296 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1903,12 +1903,6 @@
"group": "WM_DEBUG_FOCUS_LIGHT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "123161180": {
- "message": "SEVER CHILDREN",
- "level": "INFO",
- "group": "WM_SHOW_TRANSACTIONS",
- "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
- },
"140319294": {
"message": "IME target changed within ActivityRecord",
"level": "DEBUG",
@@ -2143,6 +2137,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "332390227": {
+ "message": "Sandbox max bounds for uid %s to bounds %s due to letterboxing? %s mismatch with parent bounds? %s size compat mode %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_CONFIGURATION",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"342460966": {
"message": "DRAG %s: pos=(%d,%d)",
"level": "INFO",
@@ -2623,6 +2623,12 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/WindowOrganizerController.java"
},
+ "910200295": {
+ "message": "Sandbox max bounds due to mismatched orientation with parent, to %s vs DisplayArea %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_CONFIGURATION",
+ "at": "com\/android\/server\/wm\/Task.java"
+ },
"913494177": {
"message": "removeAllWindowsIfPossible: removing win=%s",
"level": "WARN",
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/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index 3694d63..d2678c7 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -215,7 +215,8 @@
// Keystore 1.0 does not tell us the exact security level of the key
// so we report an unknown but secure security level.
insideSecureHardware ? KeyProperties.SECURITY_LEVEL_UNKNOWN_SECURE
- : KeyProperties.SECURITY_LEVEL_SOFTWARE);
+ : KeyProperties.SECURITY_LEVEL_SOFTWARE,
+ KeyProperties.UNRESTRICTED_USAGE_COUNT);
}
private static BigInteger getGateKeeperSecureUserId() throws ProviderException {
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index e9aac7d..c2a7b2e 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -274,6 +274,7 @@
private final boolean mUserConfirmationRequired;
private final boolean mUnlockedDeviceRequired;
private final boolean mCriticalToDeviceEncryption;
+ private final int mMaxUsageCount;
/*
* ***NOTE***: All new fields MUST also be added to the following:
* ParcelableKeyGenParameterSpec class.
@@ -313,7 +314,8 @@
boolean isStrongBoxBacked,
boolean userConfirmationRequired,
boolean unlockedDeviceRequired,
- boolean criticalToDeviceEncryption) {
+ boolean criticalToDeviceEncryption,
+ int maxUsageCount) {
if (TextUtils.isEmpty(keyStoreAlias)) {
throw new IllegalArgumentException("keyStoreAlias must not be empty");
}
@@ -366,6 +368,7 @@
mUserConfirmationRequired = userConfirmationRequired;
mUnlockedDeviceRequired = unlockedDeviceRequired;
mCriticalToDeviceEncryption = criticalToDeviceEncryption;
+ mMaxUsageCount = maxUsageCount;
}
/**
@@ -782,7 +785,7 @@
}
/**
- * Return whether this key is critical to the device encryption flow.
+ * Returns whether this key is critical to the device encryption flow.
*
* @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION
* @hide
@@ -792,6 +795,17 @@
}
/**
+ * Returns the maximum number of times the limited use key is allowed to be used or
+ * {@link KeyProperties#UNRESTRICTED_USAGE_COUNT} if there’s no restriction on the number of
+ * times the key can be used.
+ *
+ * @see Builder#setMaxUsageCount(int)
+ */
+ public int getMaxUsageCount() {
+ return mMaxUsageCount;
+ }
+
+ /**
* Builder of {@link KeyGenParameterSpec} instances.
*/
public final static class Builder {
@@ -827,6 +841,7 @@
private boolean mUserConfirmationRequired;
private boolean mUnlockedDeviceRequired = false;
private boolean mCriticalToDeviceEncryption = false;
+ private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT;
/**
* Creates a new instance of the {@code Builder}.
@@ -894,6 +909,7 @@
mUserConfirmationRequired = sourceSpec.isUserConfirmationRequired();
mUnlockedDeviceRequired = sourceSpec.isUnlockedDeviceRequired();
mCriticalToDeviceEncryption = sourceSpec.isCriticalToDeviceEncryption();
+ mMaxUsageCount = sourceSpec.getMaxUsageCount();
}
/**
@@ -1553,6 +1569,47 @@
}
/**
+ * Sets the maximum number of times the key is allowed to be used. After every use of the
+ * key, the use counter will decrease. This authorization applies only to secret key and
+ * private key operations. Public key operations are not restricted. For example, after
+ * successfully encrypting and decrypting data using methods such as
+ * {@link Cipher#doFinal()}, the use counter of the secret key will decrease. After
+ * successfully signing data using methods such as {@link Signature#sign()}, the use
+ * counter of the private key will decrease.
+ *
+ * When the use counter is depleted, the key will be marked for deletion by Android
+ * Keystore and any subsequent attempt to use the key will throw
+ * {@link KeyPermanentlyInvalidatedException}. There is no key to be loaded from the
+ * Android Keystore once the exhausted key is permanently deleted, as if the key never
+ * existed before.
+ *
+ * <p>By default, there is no restriction on the usage of key.
+ *
+ * <p>Some secure hardware may not support this feature at all, in which case it will
+ * be enforced in software, some secure hardware may support it but only with
+ * maxUsageCount = 1, and some secure hardware may support it with larger value
+ * of maxUsageCount.
+ *
+ * <p>The PackageManger feature flags:
+ * {@link android.content.pm.PackageManager#FEATURE_KEYSTORE_SINGLE_USE_KEY} and
+ * {@link android.content.pm.PackageManager#FEATURE_KEYSTORE_LIMITED_USE_KEY} can be used
+ * to check whether the secure hardware cannot enforce this feature, can only enforce it
+ * with maxUsageCount = 1, or can enforce it with larger value of maxUsageCount.
+ *
+ * @param maxUsageCount maximum number of times the key is allowed to be used or
+ * {@link KeyProperties#UNRESTRICTED_USAGE_COUNT} if there is no restriction on the
+ * usage.
+ */
+ @NonNull
+ public Builder setMaxUsageCount(int maxUsageCount) {
+ if (maxUsageCount == KeyProperties.UNRESTRICTED_USAGE_COUNT || maxUsageCount > 0) {
+ mMaxUsageCount = maxUsageCount;
+ return this;
+ }
+ throw new IllegalArgumentException("maxUsageCount is not valid");
+ }
+
+ /**
* Builds an instance of {@code KeyGenParameterSpec}.
*/
@NonNull
@@ -1587,7 +1644,8 @@
mIsStrongBoxBacked,
mUserConfirmationRequired,
mUnlockedDeviceRequired,
- mCriticalToDeviceEncryption);
+ mCriticalToDeviceEncryption,
+ mMaxUsageCount);
}
}
}
diff --git a/keystore/java/android/security/keystore/KeyInfo.java b/keystore/java/android/security/keystore/KeyInfo.java
index 7158d0c..f50efd2 100644
--- a/keystore/java/android/security/keystore/KeyInfo.java
+++ b/keystore/java/android/security/keystore/KeyInfo.java
@@ -85,6 +85,7 @@
private final boolean mInvalidatedByBiometricEnrollment;
private final boolean mUserConfirmationRequired;
private final @KeyProperties.SecurityLevelEnum int mSecurityLevel;
+ private final int mRemainingUsageCount;
/**
* @hide
@@ -109,7 +110,8 @@
boolean trustedUserPresenceRequired,
boolean invalidatedByBiometricEnrollment,
boolean userConfirmationRequired,
- @KeyProperties.SecurityLevelEnum int securityLevel) {
+ @KeyProperties.SecurityLevelEnum int securityLevel,
+ int remainingUsageCount) {
mKeystoreAlias = keystoreKeyAlias;
mInsideSecureHardware = insideSecureHardware;
mOrigin = origin;
@@ -134,6 +136,7 @@
mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
mUserConfirmationRequired = userConfirmationRequired;
mSecurityLevel = securityLevel;
+ mRemainingUsageCount = remainingUsageCount;
}
/**
@@ -374,4 +377,15 @@
public @KeyProperties.SecurityLevelEnum int getSecurityLevel() {
return mSecurityLevel;
}
+
+ /**
+ * Returns the remaining number of times the key is allowed to be used or
+ * {@link KeyProperties#UNRESTRICTED_USAGE_COUNT} if there's no restriction on the number of
+ * times the key can be used. Note that this gives a best effort count and need not be
+ * accurate (as there might be usages happening in parallel and the count maintained here need
+ * not be in sync with the usage).
+ */
+ public int getRemainingUsageCount() {
+ return mRemainingUsageCount;
+ }
}
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index 014d688..fa4f8b1 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -907,4 +907,9 @@
+ uid);
}
}
+
+ /**
+ * This value indicates that there is no restriction on the number of times the key can be used.
+ */
+ public static final int UNRESTRICTED_USAGE_COUNT = -1;
}
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index e1e404d..aaa3715 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -235,6 +235,7 @@
private final boolean mUserConfirmationRequired;
private final boolean mUnlockedDeviceRequired;
private final boolean mIsStrongBoxBacked;
+ private final int mMaxUsageCount;
private KeyProtection(
Date keyValidityStart,
@@ -256,7 +257,8 @@
boolean criticalToDeviceEncryption,
boolean userConfirmationRequired,
boolean unlockedDeviceRequired,
- boolean isStrongBoxBacked) {
+ boolean isStrongBoxBacked,
+ int maxUsageCount) {
mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart);
mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd);
mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd);
@@ -279,6 +281,7 @@
mUserConfirmationRequired = userConfirmationRequired;
mUnlockedDeviceRequired = unlockedDeviceRequired;
mIsStrongBoxBacked = isStrongBoxBacked;
+ mMaxUsageCount = maxUsageCount;
}
/**
@@ -548,6 +551,17 @@
}
/**
+ * Returns the maximum number of times the limited use key is allowed to be used or
+ * {@link KeyProperties#UNRESTRICTED_USAGE_COUNT} if there’s no restriction on the number of
+ * times the key can be used.
+ *
+ * @see Builder#setMaxUsageCount(int)
+ */
+ public int getMaxUsageCount() {
+ return mMaxUsageCount;
+ }
+
+ /**
* Builder of {@link KeyProtection} instances.
*/
public final static class Builder {
@@ -574,6 +588,7 @@
private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID;
private boolean mCriticalToDeviceEncryption = false;
private boolean mIsStrongBoxBacked = false;
+ private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT;
/**
* Creates a new instance of the {@code Builder}.
@@ -1039,6 +1054,47 @@
}
/**
+ * Sets the maximum number of times the key is allowed to be used. After every use of the
+ * key, the use counter will decrease. This authorization applies only to secret key and
+ * private key operations. Public key operations are not restricted. For example, after
+ * successfully encrypting and decrypting data using methods such as
+ * {@link Cipher#doFinal()}, the use counter of the secret key will decrease. After
+ * successfully signing data using methods such as {@link Signature#sign()}, the use
+ * counter of the private key will decrease.
+ *
+ * When the use counter is depleted, the key will be marked for deletion by Android
+ * Keystore and any subsequent attempt to use the key will throw
+ * {@link KeyPermanentlyInvalidatedException}. There is no key to be loaded from the
+ * Android Keystore once the exhausted key is permanently deleted, as if the key never
+ * existed before.
+ *
+ * <p>By default, there is no restriction on the usage of key.
+ *
+ * <p>Some secure hardware may not support this feature at all, in which case it will
+ * be enforced in software, some secure hardware may support it but only with
+ * maxUsageCount = 1, and some secure hardware may support it with larger value
+ * of maxUsageCount.
+ *
+ * <p>The PackageManger feature flags:
+ * {@link android.content.pm.PackageManager#FEATURE_KEYSTORE_SINGLE_USE_KEY} and
+ * {@link android.content.pm.PackageManager#FEATURE_KEYSTORE_LIMITED_USE_KEY} can be used
+ * to check whether the secure hardware cannot enforce this feature, can only enforce it
+ * with maxUsageCount = 1, or can enforce it with larger value of maxUsageCount.
+ *
+ * @param maxUsageCount maximum number of times the key is allowed to be used or
+ * {@link KeyProperties#UNRESTRICTED_USAGE_COUNT} if there is no restriction on the
+ * usage.
+ */
+ @NonNull
+ public Builder setMaxUsageCount(int maxUsageCount) {
+ if (maxUsageCount == KeyProperties.UNRESTRICTED_USAGE_COUNT || maxUsageCount > 0) {
+ mMaxUsageCount = maxUsageCount;
+ return this;
+ }
+ throw new IllegalArgumentException("maxUsageCount is not valid");
+ }
+
+ /**
* Builds an instance of {@link KeyProtection}.
*
* @throws IllegalArgumentException if a required field is missing
@@ -1065,7 +1121,8 @@
mCriticalToDeviceEncryption,
mUserConfirmationRequired,
mUnlockedDeviceRequired,
- mIsStrongBoxBacked);
+ mIsStrongBoxBacked,
+ mMaxUsageCount);
}
}
}
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
index 69c15cc..8163472 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -108,6 +108,7 @@
out.writeBoolean(mSpec.isUserConfirmationRequired());
out.writeBoolean(mSpec.isUnlockedDeviceRequired());
out.writeBoolean(mSpec.isCriticalToDeviceEncryption());
+ out.writeInt(mSpec.getMaxUsageCount());
}
private static Date readDateOrNull(Parcel in) {
@@ -166,6 +167,7 @@
final boolean userConfirmationRequired = in.readBoolean();
final boolean unlockedDeviceRequired = in.readBoolean();
final boolean criticalToDeviceEncryption = in.readBoolean();
+ final int maxUsageCount = in.readInt();
// The KeyGenParameterSpec is intentionally not constructed using a Builder here:
// The intention is for this class to break if new parameters are added to the
// KeyGenParameterSpec constructor (whereas using a builder would silently drop them).
@@ -199,7 +201,8 @@
isStrongBoxBacked,
userConfirmationRequired,
unlockedDeviceRequired,
- criticalToDeviceEncryption);
+ criticalToDeviceEncryption,
+ maxUsageCount);
}
public static final @android.annotation.NonNull Creator<ParcelableKeyGenParameterSpec> CREATOR = new Creator<ParcelableKeyGenParameterSpec>() {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
index 233f352..1575bb4 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
@@ -366,6 +366,13 @@
));
}
+ if (spec.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
+ params.add(KeyStore2ParameterUtils.makeInt(
+ KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT,
+ spec.getMaxUsageCount()
+ ));
+ }
+
byte[] additionalEntropy =
KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
mRng, (mKeySizeBits + 7) / 8);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index df0e146..6a92980 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -586,6 +586,13 @@
));
}
+ if (mSpec.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
+ params.add(KeyStore2ParameterUtils.makeInt(
+ KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT,
+ mSpec.getMaxUsageCount()
+ ));
+ }
+
addAlgorithmSpecificParameters(params);
if (mSpec.isUniqueIdIncluded()) {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
index 74503e1..fe05989 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -95,6 +95,7 @@
boolean userAuthenticationValidWhileOnBody = false;
boolean trustedUserPresenceRequired = false;
boolean trustedUserConfirmationRequired = false;
+ int remainingUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT;
try {
for (Authorization a : key.getAuthorizations()) {
switch (a.keyParameter.tag) {
@@ -195,6 +196,16 @@
trustedUserConfirmationRequired =
KeyStore2ParameterUtils.isSecureHardware(a.securityLevel);
break;
+ case KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT:
+ long remainingUsageCountUnsigned =
+ KeyStore2ParameterUtils.getUnsignedInt(a);
+ if (remainingUsageCountUnsigned > Integer.MAX_VALUE) {
+ throw new ProviderException(
+ "Usage count of limited use key too long: "
+ + remainingUsageCountUnsigned);
+ }
+ remainingUsageCount = (int) remainingUsageCountUnsigned;
+ break;
}
}
} catch (IllegalArgumentException e) {
@@ -247,7 +258,8 @@
trustedUserPresenceRequired,
invalidatedByBiometricEnrollment,
trustedUserConfirmationRequired,
- securityLevel);
+ securityLevel,
+ remainingUsageCount);
}
private static BigInteger getGateKeeperSecureUserId() throws ProviderException {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 07169ce..8c8acc4 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -535,6 +535,12 @@
spec.getKeyValidityForConsumptionEnd()
));
}
+ if (spec.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
+ importArgs.add(KeyStore2ParameterUtils.makeInt(
+ KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT,
+ spec.getMaxUsageCount()
+ ));
+ }
} catch (IllegalArgumentException | IllegalStateException e) {
throw new KeyStoreException(e);
}
@@ -772,6 +778,12 @@
params.getKeyValidityForConsumptionEnd()
));
}
+ if (params.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
+ importArgs.add(KeyStore2ParameterUtils.makeInt(
+ KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT,
+ params.getMaxUsageCount()
+ ));
+ }
} catch (IllegalArgumentException | IllegalStateException e) {
throw new KeyStoreException(e);
}
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/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 1da72f8..04d1264 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -89,6 +89,7 @@
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
mAnimationController.removeAnimator(animator.getToken());
if (mAnimationController.isAnimatorsConsumed()) {
+ resetWindowsOffsetInternal(animator.getTransitionDirection());
finishOffset(animator.getDestinationOffset(),
animator.getTransitionDirection());
}
@@ -99,6 +100,7 @@
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
mAnimationController.removeAnimator(animator.getToken());
if (mAnimationController.isAnimatorsConsumed()) {
+ resetWindowsOffsetInternal(animator.getTransitionDirection());
finishOffset(animator.getDestinationOffset(),
animator.getTransitionDirection());
}
@@ -205,6 +207,16 @@
applyTransaction(wct);
}
+ private void resetWindowsOffsetInternal(
+ @OneHandedAnimationController.TransitionDirection int td) {
+ if (td == TRANSITION_DIRECTION_TRIGGER) {
+ return;
+ }
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ resetWindowsOffset(wct);
+ applyTransaction(wct);
+ }
+
private void resetWindowsOffset(WindowContainerTransaction wct) {
final SurfaceControl.Transaction tx =
mSurfaceControlTransactionFactory.getTransaction();
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/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index 4a498d2..a57ac35 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -18,7 +18,7 @@
name: "WMShellFlickerTests",
srcs: ["src/**/*.java", "src/**/*.kt"],
manifest: "AndroidManifest.xml",
- test_config: "AndroidTestPhysicalDevices.xml",
+ test_config: "AndroidTest.xml",
platform_apis: true,
certificate: "platform",
test_suites: ["device-tests"],
@@ -36,25 +36,3 @@
"wmshell-flicker-test-components",
],
}
-
-android_test {
- name: "WMShellFlickerTestsVirtual",
- srcs: ["src/**/*.java", "src/**/*.kt"],
- manifest: "AndroidManifest.xml",
- test_config: "AndroidTestVirtualDevices.xml",
- platform_apis: true,
- certificate: "platform",
- libs: ["android.test.runner"],
- static_libs: [
- "androidx.test.ext.junit",
- "flickerlib",
- "truth-prebuilt",
- "app-helpers-core",
- "launcher-helper-lib",
- "launcher-aosp-tapl",
- "wm-flicker-common-assertions",
- "wm-flicker-common-app-helpers",
- "platform-test-annotations",
- "wmshell-flicker-test-components",
- ],
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTestPhysicalDevices.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/AndroidTestPhysicalDevices.xml
rename to libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTestVirtualDevices.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTestVirtualDevices.xml
deleted file mode 100644
index 0738608..0000000
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTestVirtualDevices.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright 2020 Google Inc. All Rights Reserved.
- -->
-<configuration description="Runs WindowManager Shell Flicker Tests">
- <option name="test-tag" value="FlickerTests" />
- <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
- <!-- keeps the screen on during tests -->
- <option name="screen-always-on" value="on" />
- <!-- prevents the phone from restarting -->
- <option name="force-skip-system-props" value="true" />
- <!-- set WM tracing verbose level to all -->
- <option name="run-command" value="cmd window tracing level all" />
- <!-- inform WM to log all transactions -->
- <option name="run-command" value="cmd window tracing transaction" />
- <!-- restart launcher to activate TAPL -->
- <option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" />
- </target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.DeviceCleaner">
- <!-- reboot the device to teardown any crashed tests -->
- <option name="cleanup-action" value="REBOOT" />
- </target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="cleanup-apks" value="true"/>
- <option name="test-file-name" value="WMShellFlickerTests.apk"/>
- <option name="test-file-name" value="WMShellFlickerTestApp.apk" />
- </target_preparer>
- <test class="com.android.tradefed.testtype.AndroidJUnitTest">
- <option name="package" value="com.android.wm.shell.flicker"/>
- <option name="exclude-annotation" value="androidx.test.filters.RequiresDevice" />
- <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
- <option name="shell-timeout" value="6600s" />
- <option name="test-timeout" value="6000s" />
- <option name="hidden-api-checks" value="false" />
- </test>
- <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
- <option name="directory-keys" value="/sdcard/flicker" />
- <option name="collect-on-run-ended-only" value="true" />
- <option name="clean-up" value="true" />
- </metrics_collector>
-</configuration>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 24b0f30..7ad7553 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -18,14 +18,14 @@
import android.graphics.Region
import android.view.Surface
+import com.android.server.wm.flicker.dsl.LayersAssertionBuilderLegacy
import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER
import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
-import com.android.server.wm.flicker.dsl.LayersAssertionBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.traces.layers.getVisibleBounds
@JvmOverloads
-fun LayersAssertionBuilder.appPairsDividerIsVisible(
+fun LayersAssertionBuilderLegacy.appPairsDividerIsVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -35,7 +35,7 @@
}
@JvmOverloads
-fun LayersAssertionBuilder.appPairsDividerIsInvisible(
+fun LayersAssertionBuilderLegacy.appPairsDividerIsInvisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -45,7 +45,7 @@
}
@JvmOverloads
-fun LayersAssertionBuilder.appPairsDividerBecomesVisible(
+fun LayersAssertionBuilderLegacy.appPairsDividerBecomesVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -57,7 +57,7 @@
}
@JvmOverloads
-fun LayersAssertionBuilder.dockedStackDividerIsVisible(
+fun LayersAssertionBuilderLegacy.dockedStackDividerIsVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -67,7 +67,7 @@
}
@JvmOverloads
-fun LayersAssertionBuilder.dockedStackDividerBecomesVisible(
+fun LayersAssertionBuilderLegacy.dockedStackDividerBecomesVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -79,7 +79,7 @@
}
@JvmOverloads
-fun LayersAssertionBuilder.dockedStackDividerBecomesInvisible(
+fun LayersAssertionBuilderLegacy.dockedStackDividerBecomesInvisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -91,7 +91,7 @@
}
@JvmOverloads
-fun LayersAssertionBuilder.dockedStackDividerIsInvisible(
+fun LayersAssertionBuilderLegacy.dockedStackDividerIsInvisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -101,7 +101,7 @@
}
@JvmOverloads
-fun LayersAssertionBuilder.appPairsPrimaryBoundsIsVisible(
+fun LayersAssertionBuilderLegacy.appPairsPrimaryBoundsIsVisible(
rotation: Int,
primaryLayerName: String,
bugId: Int = 0,
@@ -114,7 +114,7 @@
}
@JvmOverloads
-fun LayersAssertionBuilder.appPairsSecondaryBoundsIsVisible(
+fun LayersAssertionBuilderLegacy.appPairsSecondaryBoundsIsVisible(
rotation: Int,
secondaryLayerName: String,
bugId: Int = 0,
@@ -127,7 +127,7 @@
}
@JvmOverloads
-fun LayersAssertionBuilder.dockedStackPrimaryBoundsIsVisible(
+fun LayersAssertionBuilderLegacy.dockedStackPrimaryBoundsIsVisible(
rotation: Int,
primaryLayerName: String,
bugId: Int = 0,
@@ -140,7 +140,7 @@
}
@JvmOverloads
-fun LayersAssertionBuilder.dockedStackSecondaryBoundsIsVisible(
+fun LayersAssertionBuilderLegacy.dockedStackSecondaryBoundsIsVisible(
rotation: Int,
secondaryLayerName: String,
bugId: Int = 0,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
index 7191e8e..aafa9bf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
@@ -27,11 +27,9 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.buildTestTag
-import com.android.server.wm.flicker.helpers.isRotated
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.appPairsDividerIsVisible
@@ -73,13 +71,14 @@
assertions {
layersTrace {
navBarLayerRotatesAndScales(Surface.ROTATION_0, configuration.endRotation,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerRotatesScales(Surface.ROTATION_0, configuration.endRotation)
- appPairsDividerIsVisible()
+ enabled = false)
+ statusBarLayerRotatesScales(Surface.ROTATION_0, configuration.endRotation,
+ enabled = false)
+ appPairsDividerIsVisible(enabled = false)
appPairsPrimaryBoundsIsVisible(configuration.endRotation,
- primaryApp.defaultWindowName, 172776659)
+ primaryApp.defaultWindowName, bugId = 172776659)
appPairsSecondaryBoundsIsVisible(configuration.endRotation,
- secondaryApp.defaultWindowName, 172776659)
+ secondaryApp.defaultWindowName, bugId = 172776659)
}
windowManagerTrace {
navBarWindowIsAlwaysVisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
index 6aed83f..7782364 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
@@ -78,7 +78,8 @@
statusBarWindowIsAlwaysVisible()
visibleWindowsShownMoreThanOneConsecutiveEntry(
listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName)
+ secondaryApp.defaultWindowName),
+ bugId = 178447631
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt
index 42c7b7c..f2a7cda 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt
@@ -86,7 +86,8 @@
listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME,
LETTERBOX_NAME, TOAST_NAME,
splitScreenApp.defaultWindowName,
- nonResizeableApp.defaultWindowName)
+ nonResizeableApp.defaultWindowName),
+ bugId = 178447631
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt
index 5b8ec1e..421ecff 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt
@@ -75,7 +75,8 @@
LAUNCHER_PACKAGE_NAME,
LETTERBOX_NAME,
nonResizeableApp.defaultWindowName,
- splitScreenApp.defaultWindowName)
+ splitScreenApp.defaultWindowName),
+ bugId = 178447631
)
}
windowManagerTrace {
@@ -86,7 +87,8 @@
LAUNCHER_PACKAGE_NAME,
LETTERBOX_NAME,
nonResizeableApp.defaultWindowName,
- splitScreenApp.defaultWindowName)
+ splitScreenApp.defaultWindowName),
+ bugId = 178447631
)
}
}
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/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index bcc846b..bb1dbd4 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -739,6 +739,13 @@
if (mBundle != null) {
aa.mBundle = new Bundle(mBundle);
}
+
+ // Allow the FLAG_HW_HOTWORD only for AudioSource.VOICE_RECOGNITION
+ if (mSource != MediaRecorder.AudioSource.VOICE_RECOGNITION
+ && (mFlags & FLAG_HW_HOTWORD) == FLAG_HW_HOTWORD) {
+ aa.mFlags &= ~FLAG_HW_HOTWORD;
+ }
+
return aa;
}
@@ -852,6 +859,23 @@
}
/**
+ * @hide
+ * Request for capture in hotword mode.
+ *
+ * Requests an audio path optimized for Hotword detection use cases from
+ * the low power audio DSP. This is valid only for capture with
+ * audio source {@link MediaRecorder.AudioSource#VOICE_RECOGNITION}.
+ * There is no guarantee that this mode is available on the device.
+ * @return the same Builder instance.
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD)
+ public @NonNull Builder setHotwordMode() {
+ mFlags |= FLAG_HW_HOTWORD;
+ return this;
+ }
+
+ /**
* Specifies whether the audio may or may not be captured by other apps or the system.
*
* The default is {@link AudioAttributes#ALLOW_CAPTURE_BY_ALL}.
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/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 68237de..5e732f9 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -879,38 +879,37 @@
/**
* Interface for receiving events about media routing changes.
*/
- public static class Callback {
-
+ public interface Callback {
/**
* Called when routes are added.
* @param routes the list of routes that have been added. It's never empty.
*/
- public void onRoutesAdded(@NonNull List<MediaRoute2Info> routes) {}
+ default void onRoutesAdded(@NonNull List<MediaRoute2Info> routes) {}
/**
* Called when routes are removed.
* @param routes the list of routes that have been removed. It's never empty.
*/
- public void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) {}
+ default void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) {}
/**
* Called when routes are changed.
* @param routes the list of routes that have been changed. It's never empty.
*/
- public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {}
+ default void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {}
/**
* Called when a session is changed.
* @param session the updated session
*/
- public void onSessionUpdated(@NonNull RoutingSessionInfo session) {}
+ default void onSessionUpdated(@NonNull RoutingSessionInfo session) {}
/**
* Called when a session is released.
* @param session the released session.
* @see #releaseSession(RoutingSessionInfo)
*/
- public void onSessionReleased(@NonNull RoutingSessionInfo session) {}
+ default void onSessionReleased(@NonNull RoutingSessionInfo session) {}
/**
* Called when media is transferred.
@@ -918,13 +917,13 @@
* @param oldSession the previous session
* @param newSession the new session or {@code null} if the session is released.
*/
- public void onTransferred(@NonNull RoutingSessionInfo oldSession,
+ default void onTransferred(@NonNull RoutingSessionInfo oldSession,
@Nullable RoutingSessionInfo newSession) { }
/**
* Called when {@link #transfer(RoutingSessionInfo, MediaRoute2Info)} fails.
*/
- public void onTransferFailed(@NonNull RoutingSessionInfo session,
+ default void onTransferFailed(@NonNull RoutingSessionInfo session,
@NonNull MediaRoute2Info route) { }
/**
@@ -933,7 +932,7 @@
* @param packageName the package name of the application
* @param preferredFeatures the list of preferred route features set by an application.
*/
- public void onPreferredFeaturesChanged(@NonNull String packageName,
+ default void onPreferredFeaturesChanged(@NonNull String packageName,
@NonNull List<String> preferredFeatures) {}
/**
@@ -946,7 +945,7 @@
* {@link MediaRoute2ProviderService#REASON_ROUTE_NOT_AVAILABLE},
* {@link MediaRoute2ProviderService#REASON_INVALID_COMMAND},
*/
- public void onRequestFailed(int reason) {}
+ default void onRequestFailed(int reason) {}
}
final class CallbackRecord {
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index bdc8a4f..6b78817 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -22,16 +22,27 @@
#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::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;
namespace android {
@@ -294,6 +305,10 @@
return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_STATE));
}
+ if (filterEvents.size() == 0) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_ARGUMENT));
+ }
+
DemuxFilterEvent event;
DemuxFilterEventExt eventExt;
getHidlFilterEvent(filterEvents, event, eventExt);
@@ -310,80 +325,569 @@
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;
+ }
-void TunerFilterCallback::getHidlFilterEvent(const vector<TunerFilterEvent>& filterEvents,
- DemuxFilterEvent& event, DemuxFilterEventExt& /*eventExt*/) {
- // TODO: finish handling extended evets and other filter event types
- switch (filterEvents[0].getTag()) {
- case TunerFilterEvent::media: {
- for (int i = 0; i < filterEvents.size(); i++) {
- hidl_handle handle = hidl_handle(
- makeFromAidl(filterEvents[i].get<TunerFilterEvent::media>().avMemory));
- int size = event.events.size();
- event.events.resize(size + 1);
- event.events[size].media({
- .avMemory = handle,
- .streamId = static_cast<DemuxStreamId>(
- filterEvents[i].get<TunerFilterEvent::media>().streamId),
- .isPtsPresent =
- filterEvents[i].get<TunerFilterEvent::media>().isPtsPresent,
- .pts = static_cast<uint64_t>(
- filterEvents[i].get<TunerFilterEvent::media>().pts),
- .dataLength = static_cast<uint32_t>(
- filterEvents[i].get<TunerFilterEvent::media>().dataLength),
- .offset = static_cast<uint32_t>(
- filterEvents[i].get<TunerFilterEvent::media>().offset),
- .isSecureMemory =
- filterEvents[i].get<TunerFilterEvent::media>().isSecureMemory,
- .avDataId = static_cast<uint64_t>(
- filterEvents[i].get<TunerFilterEvent::media>().avDataId),
- .mpuSequenceNumber = static_cast<uint32_t>(
- filterEvents[i].get<TunerFilterEvent::media>().offset),
- .isPesPrivateData =
- filterEvents[i].get<TunerFilterEvent::media>().isPesPrivateData,
- });
- }
+ 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) {
+ switch (filterEvents[0].getTag()) {
+ case TunerFilterEvent::media: {
+ getHidlMediaEvent(filterEvents, event);
+ break;
+ }
+ case TunerFilterEvent::section: {
+ getHidlSectionEvent(filterEvents, event);
+ break;
+ }
+ case TunerFilterEvent::pes: {
+ getHidlPesEvent(filterEvents, event);
+ break;
+ }
+ case TunerFilterEvent::tsRecord: {
+ getHidlTsRecordEvent(filterEvents, event, eventExt);
+ break;
+ }
+ case TunerFilterEvent::mmtpRecord: {
+ getHidlMmtpRecordEvent(filterEvents, event, eventExt);
+ break;
+ }
+ case TunerFilterEvent::download: {
+ getHidlDownloadEvent(filterEvents, event);
+ break;
+ }
+ case TunerFilterEvent::ipPayload: {
+ getHidlIpPayloadEvent(filterEvents, event);
+ break;
+ }
+ case TunerFilterEvent::temi: {
+ getHidlTemiEvent(filterEvents, event);
+ break;
+ }
+ case TunerFilterEvent::monitor: {
+ getHidlMonitorEvent(filterEvents, eventExt);
+ break;
+ }
+ case TunerFilterEvent::startId: {
+ getHidlRestartEvent(filterEvents, eventExt);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void TunerFilterCallback::getHidlMediaEvent(
+ const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
+ for (int i = 0; i < filterEvents.size(); i++) {
+ hidl_handle handle = hidl_handle(makeFromAidl(filterEvents[i]
+ .get<TunerFilterEvent::media>().avMemory));
+ event.events.resize(i + 1);
+ event.events[i].media({
+ .avMemory = handle,
+ .streamId = static_cast<DemuxStreamId>(filterEvents[i]
+ .get<TunerFilterEvent::media>().streamId),
+ .isPtsPresent = filterEvents[i]
+ .get<TunerFilterEvent::media>().isPtsPresent,
+ .pts = static_cast<uint64_t>(filterEvents[i]
+ .get<TunerFilterEvent::media>().pts),
+ .dataLength = static_cast<uint32_t>(filterEvents[i]
+ .get<TunerFilterEvent::media>().dataLength),
+ .offset = static_cast<uint32_t>(filterEvents[i]
+ .get<TunerFilterEvent::media>().offset),
+ .isSecureMemory = filterEvents[i]
+ .get<TunerFilterEvent::media>().isSecureMemory,
+ .avDataId = static_cast<uint64_t>(filterEvents[i]
+ .get<TunerFilterEvent::media>().avDataId),
+ .mpuSequenceNumber = static_cast<uint32_t>(filterEvents[i]
+ .get<TunerFilterEvent::media>().offset),
+ .isPesPrivateData = filterEvents[i]
+ .get<TunerFilterEvent::media>().isPesPrivateData,
+ });
+
+ if (filterEvents[i].get<TunerFilterEvent::media>().isAudioExtraMetaData) {
+ event.events[i].media().extraMetaData.audio({
+ .adFade = static_cast<uint8_t>(filterEvents[i]
+ .get<TunerFilterEvent::media>().audio.adFade),
+ .adPan = static_cast<uint8_t>(filterEvents[i]
+ .get<TunerFilterEvent::media>().audio.adPan),
+ .versionTextTag = static_cast<uint8_t>(filterEvents[i]
+ .get<TunerFilterEvent::media>().audio.versionTextTag),
+ .adGainCenter = static_cast<uint8_t>(filterEvents[i]
+ .get<TunerFilterEvent::media>().audio.adGainCenter),
+ .adGainFront = static_cast<uint8_t>(filterEvents[i]
+ .get<TunerFilterEvent::media>().audio.adGainFront),
+ .adGainSurround = static_cast<uint8_t>(filterEvents[i]
+ .get<TunerFilterEvent::media>().audio.adGainSurround),
+ });
+ } else {
+ event.events[i].media().extraMetaData.noinit();
+ }
+ }
+}
+
+void TunerFilterCallback::getHidlSectionEvent(
+ const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
+ for (int i = 0; i < filterEvents.size(); i++) {
+ auto section = filterEvents[i].get<TunerFilterEvent::section>();
+ event.events.resize(i + 1);
+ event.events[i].section({
+ .tableId = static_cast<uint16_t>(section.tableId),
+ .version = static_cast<uint16_t>(section.version),
+ .sectionNum = static_cast<uint16_t>(section.sectionNum),
+ .dataLength = static_cast<uint16_t>(section.dataLength),
+ });
+ }
+}
+
+void TunerFilterCallback::getHidlPesEvent(
+ const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
+ for (int i = 0; i < filterEvents.size(); i++) {
+ auto pes = filterEvents[i].get<TunerFilterEvent::pes>();
+ event.events.resize(i + 1);
+ event.events[i].pes({
+ .streamId = static_cast<DemuxStreamId>(pes.streamId),
+ .dataLength = static_cast<uint16_t>(pes.dataLength),
+ .mpuSequenceNumber = static_cast<uint32_t>(pes.mpuSequenceNumber),
+ });
+ }
+}
+
+void TunerFilterCallback::getHidlTsRecordEvent(const vector<TunerFilterEvent>& filterEvents,
+ DemuxFilterEvent& event, DemuxFilterEventExt& eventExt) {
+ for (int i = 0; i < filterEvents.size(); i++) {
+ auto ts = filterEvents[i].get<TunerFilterEvent::tsRecord>();
+ event.events.resize(i + 1);
+ event.events[i].tsRecord({
+ .tsIndexMask = static_cast<uint32_t>(ts.tsIndexMask),
+ .byteNumber = static_cast<uint64_t>(ts.byteNumber),
+ });
+ event.events[i].tsRecord().pid.tPid(static_cast<DemuxTpid>(ts.pid));
+
+ switch (ts.scIndexMask.getTag()) {
+ case TunerFilterScIndexMask::sc: {
+ event.events[i].tsRecord().scIndexMask.sc(
+ ts.scIndexMask.get<TunerFilterScIndexMask::sc>());
+ break;
+ }
+ case TunerFilterScIndexMask::scHevc: {
+ event.events[i].tsRecord().scIndexMask.scHevc(
+ ts.scIndexMask.get<TunerFilterScIndexMask::scHevc>());
+ break;
+ }
+ default:
+ break;
+ }
+
+ eventExt.events.resize(i + 1);
+ if (ts.isExtended) {
+ eventExt.events[i].tsRecord({
+ .pts = static_cast<uint64_t>(ts.pts),
+ .firstMbInSlice = static_cast<uint32_t>(ts.firstMbInSlice),
+ });
+ } else {
+ eventExt.events[i].noinit();
+ }
+ }
+}
+
+void TunerFilterCallback::getHidlMmtpRecordEvent(const vector<TunerFilterEvent>& filterEvents,
+ DemuxFilterEvent& event, DemuxFilterEventExt& eventExt) {
+ for (int i = 0; i < filterEvents.size(); i++) {
+ auto mmtp = filterEvents[i].get<TunerFilterEvent::mmtpRecord>();
+ event.events.resize(i + 1);
+ event.events[i].mmtpRecord({
+ .scHevcIndexMask = static_cast<uint32_t>(mmtp.scHevcIndexMask),
+ .byteNumber = static_cast<uint64_t>(mmtp.byteNumber),
+ });
+
+ eventExt.events.resize(i + 1);
+ if (mmtp.isExtended) {
+ eventExt.events[i].mmtpRecord({
+ .pts = static_cast<uint64_t>(mmtp.pts),
+ .mpuSequenceNumber = static_cast<uint32_t>(mmtp.mpuSequenceNumber),
+ .firstMbInSlice = static_cast<uint32_t>(mmtp.firstMbInSlice),
+ .tsIndexMask = static_cast<uint32_t>(mmtp.tsIndexMask),
+ });
+ } else {
+ eventExt.events[i].noinit();
+ }
+ }
+}
+
+void TunerFilterCallback::getHidlDownloadEvent(const vector<TunerFilterEvent>& filterEvents,
+ DemuxFilterEvent& event) {
+ for (int i = 0; i < filterEvents.size(); i++) {
+ auto download = filterEvents[i].get<TunerFilterEvent::download>();
+ event.events.resize(i + 1);
+ event.events[i].download({
+ .itemId = static_cast<uint32_t>(download.itemId),
+ .mpuSequenceNumber = static_cast<uint32_t>(download.mpuSequenceNumber),
+ .itemFragmentIndex = static_cast<uint32_t>(download.itemFragmentIndex),
+ .lastItemFragmentIndex = static_cast<uint32_t>(download.lastItemFragmentIndex),
+ .dataLength = static_cast<uint16_t>(download.dataLength),
+ });
+ }
+}
+
+void TunerFilterCallback::getHidlIpPayloadEvent(const vector<TunerFilterEvent>& filterEvents,
+ DemuxFilterEvent& event) {
+ for (int i = 0; i < filterEvents.size(); i++) {
+ auto ip = filterEvents[i].get<TunerFilterEvent::ipPayload>();
+ event.events.resize(i + 1);
+ event.events[i].ipPayload({
+ .dataLength = static_cast<uint16_t>(ip.dataLength),
+ });
+ }
+}
+
+void TunerFilterCallback::getHidlTemiEvent(const vector<TunerFilterEvent>& filterEvents,
+ DemuxFilterEvent& event) {
+ for (int i = 0; i < filterEvents.size(); i++) {
+ auto temi = filterEvents[i].get<TunerFilterEvent::temi>();
+ event.events.resize(i + 1);
+ event.events[i].temi({
+ .pts = static_cast<uint64_t>(temi.pts),
+ .descrTag = static_cast<uint8_t>(temi.descrTag),
+ });
+ vector<uint8_t> descrData(temi.descrData.size());
+ copy(temi.descrData.begin(), temi.descrData.end(), descrData.begin());
+ }
+}
+
+void TunerFilterCallback::getHidlMonitorEvent(const vector<TunerFilterEvent>& filterEvents,
+ DemuxFilterEventExt& eventExt) {
+ auto monitor = filterEvents[0].get<TunerFilterEvent::monitor>();
+ eventExt.events.resize(1);
+ switch (monitor.getTag()) {
+ case TunerFilterMonitorEvent::scramblingStatus: {
+ eventExt.events[0].monitorEvent().scramblingStatus(
+ static_cast<ScramblingStatus>(monitor.scramblingStatus));
+ break;
+ }
+ case TunerFilterMonitorEvent::cid: {
+ eventExt.events[0].monitorEvent().cid(static_cast<uint32_t>(monitor.cid));
+ break;
+ }
+ default:
+ eventExt.events[0].noinit();
+ break;
+ }
+}
+
+void TunerFilterCallback::getHidlRestartEvent(const vector<TunerFilterEvent>& filterEvents,
+ DemuxFilterEventExt& eventExt) {
+ uint32_t startId = filterEvents[0].get<TunerFilterEvent::startId>();
+ eventExt.events.resize(1);
+ eventExt.events[0].startId(static_cast<uint32_t>(startId));
}
Result FilterClient::getFilterMq() {
diff --git a/media/jni/tuner/FilterClient.h b/media/jni/tuner/FilterClient.h
index f5539e0..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;
@@ -71,6 +88,26 @@
private:
void getHidlFilterEvent(const vector<TunerFilterEvent>& filterEvents,
DemuxFilterEvent& event, DemuxFilterEventExt& eventExt);
+ void getHidlMediaEvent(
+ const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event);
+ void getHidlSectionEvent(
+ const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event);
+ void getHidlPesEvent(
+ const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event);
+ void getHidlTsRecordEvent(const vector<TunerFilterEvent>& filterEvents,
+ DemuxFilterEvent& event, DemuxFilterEventExt& eventExt);
+ void getHidlMmtpRecordEvent(const vector<TunerFilterEvent>& filterEvents,
+ DemuxFilterEvent& event, DemuxFilterEventExt& eventExt);
+ void getHidlDownloadEvent(
+ const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event);
+ void getHidlIpPayloadEvent(
+ const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event);
+ void getHidlTemiEvent(
+ const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event);
+ void getHidlMonitorEvent(
+ const vector<TunerFilterEvent>& filterEvents, DemuxFilterEventExt& eventExt);
+ void getHidlRestartEvent(
+ const vector<TunerFilterEvent>& filterEvents, DemuxFilterEventExt& eventExt);
sp<FilterClientCallback> mFilterClientCallback;
};
@@ -181,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/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index 1286fc1..4b0062b 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -256,7 +256,7 @@
CountDownLatch latch = new CountDownLatch(1);
- addManagerCallback(new MediaRouter2Manager.Callback());
+ addManagerCallback(new MediaRouter2Manager.Callback() {});
addRouterCallback(new MediaRouter2.RouteCallback() {});
addTransferCallback(new MediaRouter2.TransferCallback() {
@Override
@@ -530,7 +530,7 @@
@Test
public void testSetSystemRouteVolume() throws Exception {
// ensure client
- addManagerCallback(new MediaRouter2Manager.Callback());
+ addManagerCallback(new MediaRouter2Manager.Callback() {});
String selectedSystemRouteId =
MediaRouter2Utils.getOriginalId(
mManager.getActiveSessions().get(0).getSelectedRoutes().get(0));
@@ -902,7 +902,7 @@
private void releaseAllSessions() {
// ensure ManagerRecord in MediaRouter2ServiceImpl
- addManagerCallback(new MediaRouter2Manager.Callback());
+ addManagerCallback(new MediaRouter2Manager.Callback() {});
for (RoutingSessionInfo session : mManager.getActiveSessions()) {
mManager.releaseSession(session);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index f83c7a2..6cf53d0b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -479,7 +479,7 @@
}
}
- class RouterManagerCallback extends MediaRouter2Manager.Callback {
+ class RouterManagerCallback implements MediaRouter2Manager.Callback {
@Override
public void onRoutesAdded(List<MediaRoute2Info> routes) {
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 60620bd..e92591d 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -38,6 +38,7 @@
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
Settings.Secure.ADAPTIVE_SLEEP,
+ Settings.Secure.CAMERA_AUTOROTATE,
Settings.Secure.AUTOFILL_SERVICE,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 66165b6..ad6a531 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -147,5 +147,10 @@
VALIDATORS.put(Global.DEVELOPMENT_SETTINGS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.NOTIFICATION_FEEDBACK_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.RESTRICTED_NETWORKING_MODE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(
+ Global.ONE_HANDED_KEYGUARD_SIDE,
+ new InclusiveIntegerRangeValidator(
+ /* first= */Global.ONE_HANDED_KEYGUARD_SIDE_LEFT,
+ /* last= */Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT));
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 3f46262..ed2b6c9 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -57,6 +57,7 @@
VALIDATORS.put(
Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ADAPTIVE_SLEEP, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.CAMERA_AUTOROTATE, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.AUTOFILL_SERVICE, NULLABLE_COMPONENT_NAME_VALIDATOR);
VALIDATORS.put(
Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 83ded43..1ce738e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1969,6 +1969,12 @@
Settings.Secure.BLUETOOTH_ON_WHILE_DRIVING,
SecureSettingsProto.BLUETOOTH_ON_WHILE_DRIVING);
+ final long smartAutoRotateToken = p.start(SecureSettingsProto.CAMERA_AUTOROTATE);
+ dumpSetting(s, p,
+ Settings.Secure.CAMERA_AUTOROTATE,
+ SecureSettingsProto.CameraAutorotate.ENABLED);
+ p.end(smartAutoRotateToken);
+
final long cameraToken = p.start(SecureSettingsProto.CAMERA);
dumpSetting(s, p,
Settings.Secure.CAMERA_GESTURE_DISABLED,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 438cec8..c11877a 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -283,6 +283,7 @@
Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS,
Settings.Global.EUICC_SWITCH_SLOT_TIMEOUT_MILLIS,
Settings.Global.FANCY_IME_ANIMATIONS,
+ Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
Settings.Global.FORCED_APP_STANDBY_ENABLED,
Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
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-keyguard/drawable/kg_emergency_button_background.xml b/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml
index 99c70a5..b96c07e 100644
--- a/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml
+++ b/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml
@@ -17,9 +17,8 @@
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?attr/wallpaperTextColorSecondary">
<item android:id="@android:id/background">
- <shape
- android:color="@android:color/transparent">
- <stroke android:width="1dp" android:color="?android:attr/textColorSecondary"/>
+ <shape android:shape="rectangle">
+ <solid android:color="?android:attr/colorAccent"/>
<corners android:radius="24dp"/>
</shape>
</item>
@@ -29,4 +28,4 @@
<corners android:radius="24dp"/>
</shape>
</item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
index 7986809..b5f55af 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
@@ -24,7 +24,7 @@
<include
style="@style/BouncerSecurityContainer"
layout="@layout/keyguard_host_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
</FrameLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
index 370576b..0ee1b69 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
@@ -43,7 +43,7 @@
<com.android.keyguard.EmergencyButton
android:id="@+id/emergency_call_button"
android:layout_width="wrap_content"
- android:layout_height="32dp"
+ android:layout_height="48dp"
android:layout_marginBottom="12dp"
android:text="@*android:string/lockscreen_emergency_call"
style="@style/Keyguard.TextView.EmergencyButton" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
index 04e645b..c75ee51 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
@@ -41,13 +41,14 @@
android:layout_gravity="center">
<com.android.keyguard.KeyguardSecurityViewFlipper
android:id="@+id/view_flipper"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingTop="@dimen/keyguard_security_view_top_margin"
android:paddingStart="@dimen/keyguard_security_view_lateral_margin"
android:paddingEnd="@dimen/keyguard_security_view_lateral_margin"
+ android:layout_gravity="center"
android:gravity="center">
</com.android.keyguard.KeyguardSecurityViewFlipper>
</com.android.keyguard.KeyguardSecurityContainer>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
index dc2d11d..1a38585 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
@@ -67,6 +67,7 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="bottom|center_horizontal"
+ android:layout_marginTop="@dimen/keyguard_eca_top_margin"
android:gravity="center_horizontal" />
</LinearLayout>
</FrameLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index aa14645a..6ae759c 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -188,6 +188,7 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="bottom|center_horizontal"
+ android:layout_marginTop="@dimen/keyguard_eca_top_margin"
android:gravity="center_horizontal"/>
</com.android.keyguard.KeyguardPINView>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
index 64ccefd..f709424 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
@@ -198,6 +198,7 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="bottom|center_horizontal"
+ android:layout_marginTop="@dimen/keyguard_eca_top_margin"
android:gravity="center_horizontal"/>
</com.android.keyguard.KeyguardSimPinView>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
index 70f495c..2f9fed6 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
@@ -199,5 +199,6 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="bottom|center_horizontal"
+ android:layout_marginTop="@dimen/keyguard_eca_top_margin"
android:gravity="center_horizontal"/>
</com.android.keyguard.KeyguardSimPukView>
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml b/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
new file mode 100644
index 0000000..e09bf7e
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <bool name="can_use_one_handed_bouncer">true</bool>
+</resources>
diff --git a/packages/SystemUI/res-keyguard/values/config.xml b/packages/SystemUI/res-keyguard/values/config.xml
index 8d9d6ee..6176f7c 100644
--- a/packages/SystemUI/res-keyguard/values/config.xml
+++ b/packages/SystemUI/res-keyguard/values/config.xml
@@ -22,4 +22,5 @@
<!-- Allow the menu hard key to be disabled in LockScreen on some devices [DO NOT TRANSLATE] -->
<bool name="config_disableMenuKeyInLockScreen">false</bool>
+ <bool name="can_use_one_handed_bouncer">false</bool>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index aa87107..a928b75 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -40,6 +40,8 @@
<dimen name="keyguard_security_view_top_margin">8dp</dimen>
<dimen name="keyguard_security_view_lateral_margin">36dp</dimen>
+ <dimen name="keyguard_eca_top_margin">24dp</dimen>
+
<!-- EmergencyCarrierArea overlap - amount to overlap the emergency button and carrier text.
Should be 0 on devices with plenty of room (e.g. tablets) -->
<dimen name="eca_overlap">-10dip</dimen>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 2e99dea..cd82b80 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -23,12 +23,13 @@
<item name="android:textSize">@dimen/kg_status_line_font_size</item>
</style>
<style name="Keyguard.TextView.EmergencyButton" parent="Theme.SystemUI">
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
<item name="android:textSize">14dp</item>
<item name="android:background">@drawable/kg_emergency_button_background</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:paddingLeft">12dp</item>
<item name="android:paddingRight">12dp</item>
+ <item name="android:stateListAnimator">@null</item>
</style>
<style name="NumPadKey" parent="Theme.SystemUI">
<item name="android:colorControlNormal">?android:attr/colorBackground</item>
diff --git a/packages/SystemUI/res/layout-land/global_screenshot_preview.xml b/packages/SystemUI/res/layout-land/global_screenshot_preview.xml
index 71b414f..93664da 100644
--- a/packages/SystemUI/res/layout-land/global_screenshot_preview.xml
+++ b/packages/SystemUI/res/layout-land/global_screenshot_preview.xml
@@ -25,7 +25,7 @@
android:layout_marginBottom="@dimen/screenshot_offset_y"
android:scaleType="fitStart"
android:elevation="@dimen/screenshot_preview_elevation"
- android:visibility="gone"
+ android:visibility="invisible"
android:background="@drawable/screenshot_rounded_corners"
android:adjustViewBounds="true"
android:contentDescription="@string/screenshot_edit_label"
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 cb7327f..81b7a7b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1888,7 +1888,7 @@
<!-- Notification Inline controls: describes how the notification was adjusted [CHAR_LIMIT=NONE] -->
<string name="feedback_demoted">This notification was automatically <b>ranked lower</b> in your shade.</string>
<!-- Notification Inline controls: prompts the user for feedback [CHAR_LIMIT=NONE] -->
- <string name="feedback_prompt">Was this correct?</string>
+ <string name="feedback_prompt">Let the developer know your feedback. Was this correct?</string>
<!-- Notification Inline controls: responds to user provided feedback [CHAR_LIMIT=NONE] -->
<string name="feedback_response">Thanks for your feedback!</string>
<string name="feedback_ok">OK</string>
@@ -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>
@@ -2771,13 +2768,6 @@
<!-- Controls menu, edit [CHAR_LIMIT=30] -->
<string name="controls_menu_edit">Edit controls</string>
- <!-- Device-specific path to the node for enabling/disabling the high-brightness mode -->
- <string name="udfps_hbm_sysfs_path" translatable="false"></string>
- <!-- Device-specific payload for enabling the high-brightness mode -->
- <string name="udfps_hbm_enable_command" translatable="false"></string>
- <!-- Device-specific payload for disabling the high-brightness mode -->
- <string name="udfps_hbm_disable_command" translatable="false"></string>
-
<!-- Title for the media output group dialog with media related devices [CHAR LIMIT=50] -->
<string name="media_output_dialog_add_output">Add outputs</string>
<!-- Title for the media output slice with group devices [CHAR LIMIT=50] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index ad4e78e..a74b564 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -354,15 +354,17 @@
</style>
<style name="LockPatternStyle">
- <item name="*android:regularColor">?android:attr/textColorPrimary</item>
+ <item name="*android:regularColor">?android:attr/colorAccent</item>
<item name="*android:successColor">?android:attr/textColorPrimary</item>
<item name="*android:errorColor">?android:attr/colorError</item>
+ <item name="*android:dotColor">?android:attr/textColorSecondary</item>
</style>
<style name="LockPatternStyleBiometricPrompt">
<item name="*android:regularColor">?android:attr/colorForeground</item>
<item name="*android:successColor">?android:attr/colorForeground</item>
<item name="*android:errorColor">?android:attr/colorError</item>
+ <item name="*android:dotColor">?android:attr/textColorSecondary</item>
</style>
<style name="qs_theme" parent="@*android:style/Theme.DeviceDefault.QuickSettings">
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
index b7d7498..707ee29 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
@@ -154,7 +154,7 @@
**/
public void reloadColors() {
int color = Utils.getColorAttrDefaultColor(getContext(),
- android.R.attr.textColorSecondary);
+ android.R.attr.textColorPrimaryInverse);
setTextColor(color);
setBackground(getContext()
.getDrawable(com.android.systemui.R.drawable.kg_emergency_button_background));
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index cc59c39..a7ed672 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -29,12 +29,17 @@
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.util.MathUtils;
import android.util.TypedValue;
+import android.view.Gravity;
import android.view.MotionEvent;
+import android.view.OrientationEventListener;
import android.view.VelocityTracker;
+import android.view.View;
import android.view.ViewConfiguration;
+import android.view.ViewPropertyAnimator;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimationControlListener;
@@ -55,6 +60,7 @@
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import java.util.List;
@@ -99,6 +105,12 @@
private boolean mDisappearAnimRunning;
private SwipeListener mSwipeListener;
+ private boolean mIsSecurityViewLeftAligned = true;
+ private boolean mOneHandedMode = false;
+ private SecurityMode mSecurityMode = SecurityMode.Invalid;
+ private ViewPropertyAnimator mRunningOneHandedAnimator;
+ private final OrientationEventListener mOrientationEventListener;
+
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
@@ -157,16 +169,20 @@
// Used to notify the container when something interesting happens.
public interface SecurityCallback {
boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen);
+
void userActivity();
+
void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
/**
- * @param strongAuth wheher the user has authenticated with strong authentication like
- * pattern, password or PIN but not by trust agents or fingerprint
+ * @param strongAuth wheher the user has authenticated with strong authentication like
+ * pattern, password or PIN but not by trust agents or fingerprint
* @param targetUserId a user that needs to be the foreground user at the finish completion.
*/
void finish(boolean strongAuth, int targetUserId);
+
void reset();
+
void onCancelClicked();
}
@@ -224,11 +240,119 @@
super(context, attrs, defStyle);
mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y);
mViewConfiguration = ViewConfiguration.get(context);
+
+ mOrientationEventListener = new OrientationEventListener(context) {
+ @Override
+ public void onOrientationChanged(int orientation) {
+ updateLayoutForSecurityMode(mSecurityMode);
+ }
+ };
}
void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
+ mSecurityMode = securityMode;
mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
updateBiometricRetry(securityMode, faceAuthEnabled);
+
+ updateLayoutForSecurityMode(securityMode);
+ mOrientationEventListener.enable();
+ }
+
+ void updateLayoutForSecurityMode(SecurityMode securityMode) {
+ mSecurityMode = securityMode;
+ mOneHandedMode = canUseOneHandedBouncer();
+
+ if (mOneHandedMode) {
+ mIsSecurityViewLeftAligned = isOneHandedKeyguardLeftAligned(mContext);
+ }
+
+ updateSecurityViewGravity();
+ updateSecurityViewLocation(false);
+ }
+
+ /** Return whether the one-handed keyguard should be enabled. */
+ private boolean canUseOneHandedBouncer() {
+ // Is it enabled?
+ if (!getResources().getBoolean(
+ com.android.internal.R.bool.config_enableOneHandedKeyguard)) {
+ return false;
+ }
+
+ if (!KeyguardSecurityModel.isSecurityViewOneHanded(mSecurityMode)) {
+ return false;
+ }
+
+ return getResources().getBoolean(R.bool.can_use_one_handed_bouncer);
+ }
+
+ /** Read whether the one-handed keyguard should be on the left/right from settings. */
+ private boolean isOneHandedKeyguardLeftAligned(Context context) {
+ try {
+ return Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.ONE_HANDED_KEYGUARD_SIDE)
+ == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
+ } catch (Settings.SettingNotFoundException ex) {
+ return true;
+ }
+ }
+
+ private void updateSecurityViewGravity() {
+ View securityView = getChildAt(0);
+
+ if (securityView == null) {
+ return;
+ }
+
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) securityView.getLayoutParams();
+
+ if (mOneHandedMode) {
+ lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
+ } else {
+ lp.gravity = Gravity.CENTER;
+ }
+
+ securityView.setLayoutParams(lp);
+ }
+
+ /**
+ * Moves the inner security view to the correct location (in one handed mode) with animation.
+ * This is triggered when the user taps on the side of the screen that is not currently occupied
+ * by the security view .
+ */
+ private void updateSecurityViewLocation(boolean animate) {
+ View securityView = getChildAt(0);
+
+ if (securityView == null) {
+ return;
+ }
+
+ if (!mOneHandedMode) {
+ securityView.setTranslationX(0);
+ return;
+ }
+
+ if (mRunningOneHandedAnimator != null) {
+ mRunningOneHandedAnimator.cancel();
+ mRunningOneHandedAnimator = null;
+ }
+
+ int targetTranslation = mIsSecurityViewLeftAligned ? 0 : (int) (getMeasuredWidth() / 2f);
+
+ if (animate) {
+ mRunningOneHandedAnimator = securityView.animate().translationX(targetTranslation);
+ mRunningOneHandedAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mRunningOneHandedAnimator.setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mRunningOneHandedAnimator = null;
+ }
+ });
+
+ mRunningOneHandedAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ mRunningOneHandedAnimator.start();
+ } else {
+ securityView.setTranslationX(targetTranslation);
+ }
}
public void onPause() {
@@ -237,6 +361,7 @@
mAlertDialog = null;
}
mSecurityViewFlipper.setWindowInsetsAnimationCallback(null);
+ mOrientationEventListener.disable();
}
@Override
@@ -318,19 +443,44 @@
if (mSwipeListener != null) {
mSwipeListener.onSwipeUp();
}
+ } else {
+ if (!mIsDragging) {
+ handleTap(event);
+ }
}
}
return true;
}
+ private void handleTap(MotionEvent event) {
+ // If we're using a fullscreen security mode, skip
+ if (!mOneHandedMode) {
+ return;
+ }
+
+ // Did the tap hit the "other" side of the bouncer?
+ if ((mIsSecurityViewLeftAligned && (event.getX() > getWidth() / 2f))
+ || (!mIsSecurityViewLeftAligned && (event.getX() < getWidth() / 2f))) {
+ mIsSecurityViewLeftAligned = !mIsSecurityViewLeftAligned;
+
+ Settings.Global.putInt(
+ mContext.getContentResolver(),
+ Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
+ mIsSecurityViewLeftAligned ? Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT
+ : Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT);
+
+ updateSecurityViewLocation(true);
+ }
+ }
+
void setSwipeListener(SwipeListener swipeListener) {
mSwipeListener = swipeListener;
}
private void startSpringAnimation(float startVelocity) {
mSpringAnimation
- .setStartVelocity(startVelocity)
- .animateToFinalPosition(0);
+ .setStartVelocity(startVelocity)
+ .animateToFinalPosition(0);
}
public void startDisappearAnimation(SecurityMode securitySelection) {
@@ -395,7 +545,9 @@
}
private void beginJankInstrument(int cuj) {
- InteractionJankMonitor.getInstance().begin(mSecurityViewFlipper.getSecurityView(), cuj);
+ KeyguardInputView securityView = mSecurityViewFlipper.getSecurityView();
+ if (securityView == null) return;
+ InteractionJankMonitor.getInstance().begin(securityView, cuj);
}
private void endJankInstrument(int cuj) {
@@ -438,18 +590,17 @@
return insets.inset(0, 0, 0, inset);
}
-
private void showDialog(String title, String message) {
if (mAlertDialog != null) {
mAlertDialog.dismiss();
}
mAlertDialog = new AlertDialog.Builder(mContext)
- .setTitle(title)
- .setMessage(message)
- .setCancelable(false)
- .setNeutralButton(R.string.ok, null)
- .create();
+ .setTitle(title)
+ .setMessage(message)
+ .setCancelable(false)
+ .setNeutralButton(R.string.ok, null)
+ .create();
if (!(mContext instanceof Activity)) {
mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
}
@@ -487,6 +638,53 @@
}
}
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // This is a little hacky, but this element only ever has one wrap_content child, and is
+ // itself set to match_parent, so we can take a couple of shortcuts compared to
+ // FrameLayout#onMeasure
+ int maxHeight = 0;
+ int maxWidth = 0;
+ int childState = 0;
+
+ int count = getChildCount();
+
+ int halfWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
+ MeasureSpec.getSize(widthMeasureSpec) / 2,
+ MeasureSpec.getMode(widthMeasureSpec));
+
+ if (count > 1) {
+ throw new IllegalStateException("KeyguardSecurityContainer should only have one child");
+ }
+
+ if (count > 0) {
+ final View securityView = getChildAt(0);
+ if (securityView.getVisibility() != GONE) {
+ if (mOneHandedMode) {
+ measureChildWithMargins(securityView, halfWidthMeasureSpec, 0,
+ heightMeasureSpec, 0);
+ } else {
+ measureChildWithMargins(securityView, widthMeasureSpec, 0,
+ heightMeasureSpec, 0);
+ }
+ final LayoutParams lp = (LayoutParams) securityView.getLayoutParams();
+ maxWidth = Math.max(maxWidth,
+ securityView.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
+ maxHeight = Math.max(maxHeight,
+ securityView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
+ childState = combineMeasuredStates(childState, securityView.getMeasuredState());
+ }
+ }
+
+ // Check against our minimum height and width
+ maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
+ maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
+
+ setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
+ resolveSizeAndState(maxHeight, heightMeasureSpec,
+ childState << MEASURED_HEIGHT_STATE_SHIFT));
+ }
+
void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
String message = null;
switch (userType) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 1a8d420..fdab8db 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -404,6 +404,7 @@
if (newView != null) {
newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
mSecurityViewFlipperController.show(newView);
+ mView.updateLayoutForSecurityMode(securityMode);
}
mSecurityCallback.onSecurityModeChanged(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
index c77c867..631c248 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
@@ -92,4 +92,13 @@
throw new IllegalStateException("Unknown security quality:" + security);
}
}
+
+ /**
+ * Returns whether the given security view should be used in a "one handed" way. This can be
+ * used to change how the security view is drawn (e.g. take up less of the screen, and align to
+ * one side).
+ */
+ public static boolean isSecurityViewOneHanded(SecurityMode securityMode) {
+ return securityMode == SecurityMode.Pattern || securityMode == SecurityMode.PIN;
+ }
}
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..db29533 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;
@@ -49,6 +51,12 @@
public class ScrollCaptureController implements OnComputeInternalInsetsListener {
private static final String TAG = "ScrollCaptureController";
+ // TODO: Support saving without additional action.
+ private enum PendingAction {
+ SHARE,
+ EDIT
+ }
+
public static final int MAX_PAGES = 5;
public static final int MAX_HEIGHT = 12000;
@@ -70,9 +78,6 @@
private View mEdit;
private View mShare;
- private ListenableFuture<ImageExporter.Result> mExportFuture;
- private Runnable mPendingAction;
-
public ScrollCaptureController(Context context, Connection connection, Executor uiExecutor,
Executor bgExecutor, ImageExporter exporter, UiEventLogger uiEventLogger) {
mContext = context;
@@ -137,25 +142,17 @@
if (id == R.id.close) {
v.setPressed(true);
disableButtons();
- finish();
+ doFinish();
} else if (id == R.id.edit) {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_EDIT);
v.setPressed(true);
disableButtons();
- edit();
+ startExport(PendingAction.EDIT);
} else if (id == R.id.share) {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_SHARE);
v.setPressed(true);
disableButtons();
- share();
- }
- }
-
- private void finish() {
- if (mExportFuture == null) {
- doFinish();
- } else {
- mExportFuture.addListener(this::doFinish, mUiExecutor);
+ startExport(PendingAction.SHARE);
}
}
@@ -167,30 +164,53 @@
.removeOnComputeInternalInsetsListener(this);
}
- private void edit() {
- sendIntentWhenReady(Intent.ACTION_EDIT);
- }
-
- private void share() {
- sendIntentWhenReady(Intent.ACTION_SEND);
- }
-
- void sendIntentWhenReady(String action) {
- if (mExportFuture != null) {
- mExportFuture.addListener(() -> {
- try {
- ImageExporter.Result result = mExportFuture.get();
- sendIntent(action, result.uri);
- mCallback.onFinish();
- } catch (InterruptedException | ExecutionException e) {
- Log.e(TAG, "failed to export", e);
- mCallback.onFinish();
+ private void startExport(PendingAction action) {
+ ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export(
+ mBgExecutor, mRequestId, mImageTileSet.toBitmap(), mCaptureTime);
+ exportFuture.addListener(() -> {
+ try {
+ ImageExporter.Result result = exportFuture.get();
+ if (action == PendingAction.EDIT) {
+ doEdit(result.uri);
+ } else if (action == PendingAction.SHARE) {
+ doShare(result.uri);
}
+ doFinish();
+ } catch (InterruptedException | ExecutionException e) {
+ Log.e(TAG, "failed to export", e);
+ mCallback.onFinish();
+ }
+ }, mUiExecutor);
+ }
- }, mUiExecutor);
- } else {
- mPendingAction = this::edit;
+ private void doEdit(Uri uri) {
+ String editorPackage = mContext.getString(R.string.config_screenshotEditor);
+ Intent intent = new Intent(Intent.ACTION_EDIT);
+ if (!TextUtils.isEmpty(editorPackage)) {
+ intent.setComponent(ComponentName.unflattenFromString(editorPackage));
}
+ 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);
+ Intent sharingChooserIntent = Intent.createChooser(intent, null)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ mContext.startActivityAsUser(sharingChooserIntent, UserHandle.CURRENT);
+ }
+
+ private void doShare(Uri uri) {
+ Intent intent = new Intent(Intent.ACTION_SEND);
+ 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);
+ Intent sharingChooserIntent = Intent.createChooser(intent, null)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ mContext.startActivityAsUser(sharingChooserIntent, UserHandle.CURRENT);
}
private void setContentView(@IdRes int id) {
@@ -245,21 +265,6 @@
session.end(mCallback::onFinish);
} else {
mPreview.setImageDrawable(mImageTileSet.getDrawable());
- mExportFuture = mImageExporter.export(
- mBgExecutor, mRequestId, mImageTileSet.toBitmap(), mCaptureTime);
- // The user chose an action already, link it to the result
- if (mPendingAction != null) {
- mExportFuture.addListener(mPendingAction, mUiExecutor);
- }
}
}
-
- 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);
- }
}
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/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 93156d8..0957f78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -69,6 +69,8 @@
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogController;
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
+import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.leak.LeakDetector;
@@ -89,6 +91,10 @@
*/
@Module(includes = { NotificationSectionHeadersModule.class })
public interface NotificationsModule {
+ @Binds
+ StackScrollAlgorithm.SectionProvider bindSectionProvider(
+ NotificationSectionsManager impl);
+
/** Provides an instance of {@link NotificationEntryManager} */
@SysUISingleton
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index a12179c..756fe6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -22,6 +22,7 @@
import android.util.MathUtils;
import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -30,9 +31,12 @@
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider;
+import javax.inject.Inject;
+
/**
* A global state to track all input states for the algorithm.
*/
+@SysUISingleton
public class AmbientState {
private static final float MAX_PULSE_HEIGHT = 100000f;
@@ -83,6 +87,7 @@
/** Tracks the state from AlertingNotificationManager#hasNotifications() */
private boolean mHasAlertEntries;
+ @Inject
public AmbientState(
Context context,
@NonNull SectionProvider sectionProvider) {
@@ -98,7 +103,7 @@
mBaseZHeight = getBaseHeight(mZDistanceBetweenElements);
}
- void setIsShadeOpening(boolean isOpening) {
+ public void setIsShadeOpening(boolean isOpening) {
mIsShadeOpening = isOpening;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 3f3be44..2c7c5cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -491,7 +491,8 @@
NotificationSectionsManager notificationSectionsManager,
GroupMembershipManager groupMembershipManager,
GroupExpansionManager groupExpansionManager,
- SysuiStatusBarStateController statusbarStateController
+ SysuiStatusBarStateController statusbarStateController,
+ AmbientState ambientState
) {
super(context, attrs, 0, 0);
Resources res = getResources();
@@ -500,7 +501,7 @@
mSectionsManager.initialize(this, LayoutInflater.from(context));
mSections = mSectionsManager.createSectionsForBuckets();
- mAmbientState = new AmbientState(context, mSectionsManager);
+ mAmbientState = ambientState;
mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackgroundFloating)
.getDefaultColor();
int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
@@ -549,10 +550,6 @@
}
}
- void setIsShadeOpening(boolean isOpening) {
- mAmbientState.setIsShadeOpening(isOpening);
- }
-
void setSectionPadding(float margin) {
mAmbientState.setSectionPadding(margin);
requestChildrenUpdate();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 879dad8..175e046 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -271,10 +271,6 @@
}
};
- public void setIsShadeOpening(boolean isOpening) {
- mView.setIsShadeOpening(isOpening);
- }
-
public void setSectionPadding(float padding) {
mView.setSectionPadding(padding);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
index 4b70de9..7011eee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
@@ -127,11 +127,9 @@
mResources = resources;
mHeadsUpManagerPhone = headsUpManagerPhone;
- if (view == null) {
- return;
+ if (view != null) {
+ mKeyguardIndicationController.setLockIconController(this);
}
-
- mKeyguardIndicationController.setLockIconController(this);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index a5284f1..cf4e9be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -120,6 +120,7 @@
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
@@ -552,10 +553,12 @@
AuthController authController,
QSDetailDisplayer qsDetailDisplayer,
ScrimController scrimController,
- MediaDataManager mediaDataManager) {
+ MediaDataManager mediaDataManager,
+ AmbientState ambientState) {
super(view, falsingManager, dozeLog, keyguardStateController,
(SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
- latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager);
+ latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager,
+ ambientState);
mView = view;
mMetricsLogger = metricsLogger;
mActivityManager = activityManager;
@@ -2411,11 +2414,6 @@
}
@Override
- protected void setIsShadeOpening(boolean isOpening) {
- mNotificationStackScrollLayoutController.setIsShadeOpening(isOpening);
- }
-
- @Override
public void setSectionPadding(float padding) {
if (padding == mSectionPadding) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index bee0cb1..28cfe21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -53,7 +53,7 @@
if (DEBUG) LOG("go state: %d -> %d", mState, state);
mState = state;
if (mPanel != null) {
- mPanel.setIsShadeOpening(state == STATE_OPENING);
+ mPanel.getAmbientState().setIsShadeOpening(state == STATE_OPENING);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 3031b8a..55744f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -27,6 +27,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
+import android.annotation.Nullable;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.SystemClock;
@@ -54,6 +55,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.wm.shell.animation.FlingAnimationUtils;
@@ -145,6 +147,9 @@
private float mInitialTouchX;
private boolean mTouchDisabled;
+ // AmbientState will never be null since it provides an @Inject constructor for Dagger to call.
+ private AmbientState mAmbientState;
+
/**
* Whether or not the PanelView can be expanded or collapsed with a drag.
*/
@@ -223,13 +228,19 @@
mJustPeeked = true;
}
+ protected AmbientState getAmbientState() {
+ return mAmbientState;
+ }
+
public PanelViewController(PanelView view,
FalsingManager falsingManager, DozeLog dozeLog,
KeyguardStateController keyguardStateController,
SysuiStatusBarStateController statusBarStateController, VibratorHelper vibratorHelper,
LatencyTracker latencyTracker,
FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
- StatusBarTouchableRegionManager statusBarTouchableRegionManager) {
+ StatusBarTouchableRegionManager statusBarTouchableRegionManager,
+ AmbientState ambientState) {
+ mAmbientState = ambientState;
mView = view;
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
@@ -776,8 +787,6 @@
*/
protected abstract boolean isTrackingBlocked();
- protected abstract void setIsShadeOpening(boolean isShadeOpening);
-
protected abstract void setSectionPadding(float padding);
protected abstract void setOverExpansion(float overExpansion, boolean isPixels);
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/statusbar/notification/row/FeedbackInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
index 53ff957..fe2f5f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
@@ -146,7 +146,8 @@
mAssistantFeedbackController);
TextView prompt = mFeedbackInfo.findViewById(R.id.prompt);
assertEquals("This notification was automatically demoted to Silent by the system. "
- + "Was this correct?", prompt.getText().toString());
+ + "Let the developer know your feedback. Was this correct?",
+ prompt.getText().toString());
}
@Test
@@ -157,7 +158,8 @@
mAssistantFeedbackController);
TextView prompt = mFeedbackInfo.findViewById(R.id.prompt);
assertEquals("This notification was automatically ranked higher in your shade. "
- + "Was this correct?", prompt.getText().toString());
+ + "Let the developer know your feedback. Was this correct?",
+ prompt.getText().toString());
}
@Test
@@ -168,7 +170,7 @@
mAssistantFeedbackController);
TextView prompt = mFeedbackInfo.findViewById(R.id.prompt);
assertEquals("This notification was automatically promoted to Default by the system. "
- + "Was this correct?",
+ + "Let the developer know your feedback. Was this correct?",
prompt.getText().toString());
}
@@ -180,7 +182,8 @@
mAssistantFeedbackController);
TextView prompt = mFeedbackInfo.findViewById(R.id.prompt);
assertEquals("This notification was automatically ranked lower in your shade. "
- + "Was this correct?", prompt.getText().toString());
+ + "Let the developer know your feedback. Was this correct?",
+ prompt.getText().toString());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index ceb4d84..461f64e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -83,6 +83,7 @@
private NotificationStackScrollLayout mStackScroller; // Normally test this
private NotificationStackScrollLayout mStackScrollerInternal; // See explanation below
+ private AmbientState mAmbientState;
@Rule public MockitoRule mockito = MockitoJUnit.rule();
@Mock private StatusBar mBar;
@@ -123,6 +124,9 @@
});
when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
+ // Interact with real instance of AmbientState.
+ mAmbientState = new AmbientState(mContext, mNotificationSectionsManager);
+
// The actual class under test. You may need to work with this class directly when
// testing anonymous class members of mStackScroller, like mMenuEventListener,
// which refer to members of NotificationStackScrollLayout. The spy
@@ -134,7 +138,8 @@
mNotificationSectionsManager,
mGroupMembershipManger,
mGroupExpansionManager,
- mStatusBarStateController
+ mStatusBarStateController,
+ mAmbientState
);
mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider,
mNotificationSwipeHelper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index aa7143e..8d4470b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -80,6 +80,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
@@ -202,7 +203,8 @@
private ScrimController mScrimController;
@Mock
private MediaDataManager mMediaDataManager;
-
+ @Mock
+ private AmbientState mAmbientState;
private NotificationPanelViewController mNotificationPanelViewController;
private View.AccessibilityDelegate mAccessibiltyDelegate;
@@ -282,7 +284,8 @@
mAuthController,
new QSDetailDisplayer(),
mScrimController,
- mMediaDataManager);
+ mMediaDataManager,
+ mAmbientState);
mNotificationPanelViewController.initDependencies(
mStatusBar,
mNotificationShelfController);
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/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index c091dfa..f2e1920 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -739,11 +739,11 @@
}
private void maybeLogBroadcast(NetworkAgentInfo nai, DetailedState state, int type,
- boolean isDefaultNetwork) {
+ boolean isFallbackNetwork) {
if (DBG) {
log("Sending " + state
+ " broadcast for type " + type + " " + nai.toShortString()
- + " isDefaultNetwork=" + isDefaultNetwork);
+ + " isFallbackNetwork=" + isFallbackNetwork);
}
}
@@ -762,10 +762,10 @@
list.add(nai);
}
- // Send a broadcast if this is the first network of its type or if it's the default.
- final boolean isDefaultNetwork = mService.isDefaultNetwork(nai);
- if ((list.size() == 1) || isDefaultNetwork) {
- maybeLogBroadcast(nai, DetailedState.CONNECTED, type, isDefaultNetwork);
+ // Send a broadcast if this is the first network of its type or if it's the fallback.
+ final boolean isFallbackNetwork = mService.isFallbackNetwork(nai);
+ if ((list.size() == 1) || isFallbackNetwork) {
+ maybeLogBroadcast(nai, DetailedState.CONNECTED, type, isFallbackNetwork);
mService.sendLegacyNetworkBroadcast(nai, DetailedState.CONNECTED, type);
}
}
@@ -794,7 +794,7 @@
", sending connected broadcast");
final NetworkAgentInfo replacement = list.get(0);
maybeLogBroadcast(replacement, DetailedState.CONNECTED, type,
- mService.isDefaultNetwork(replacement));
+ mService.isFallbackNetwork(replacement));
mService.sendLegacyNetworkBroadcast(replacement, DetailedState.CONNECTED, type);
}
}
@@ -810,14 +810,14 @@
// send out another legacy broadcast - currently only used for suspend/unsuspend
// toggle
public void update(NetworkAgentInfo nai) {
- final boolean isDefault = mService.isDefaultNetwork(nai);
+ final boolean isFallback = mService.isFallbackNetwork(nai);
final DetailedState state = nai.networkInfo.getDetailedState();
for (int type = 0; type < mTypeLists.length; type++) {
final ArrayList<NetworkAgentInfo> list = mTypeLists[type];
final boolean contains = (list != null && list.contains(nai));
final boolean isFirst = contains && (nai == list.get(0));
- if (isFirst || contains && isDefault) {
- maybeLogBroadcast(nai, state, type, isDefault);
+ if (isFirst || contains && isFallback) {
+ maybeLogBroadcast(nai, state, type, isFallback);
mService.sendLegacyNetworkBroadcast(nai, state, type);
}
}
@@ -1021,11 +1021,13 @@
mNetworkRequestCounter = new PerUidCounter(MAX_NETWORK_REQUESTS_PER_UID);
mMetricsLog = logger;
- mDefaultRequest = createDefaultInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST);
mNetworkRanker = new NetworkRanker();
- NetworkRequestInfo defaultNRI = new NetworkRequestInfo(null, mDefaultRequest, new Binder());
- mNetworkRequests.put(mDefaultRequest, defaultNRI);
- mNetworkRequestInfoLogs.log("REGISTER " + defaultNRI);
+ final NetworkRequest fallbackRequest = createDefaultInternetRequestForTransport(
+ -1, NetworkRequest.Type.REQUEST);
+ mFallbackRequest = new NetworkRequestInfo(null, fallbackRequest, new Binder());
+ mNetworkRequests.put(fallbackRequest, mFallbackRequest);
+ mDefaultNetworkRequests.add(mFallbackRequest);
+ mNetworkRequestInfoLogs.log("REGISTER " + mFallbackRequest);
mDefaultMobileDataRequest = createDefaultInternetRequestForTransport(
NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST);
@@ -1364,7 +1366,7 @@
}
private NetworkState getUnfilteredActiveNetworkState(int uid) {
- NetworkAgentInfo nai = getDefaultNetwork();
+ NetworkAgentInfo nai = getFallbackNetwork();
final Network[] networks = getVpnUnderlyingNetworks(uid);
if (networks != null) {
@@ -1497,7 +1499,7 @@
}
}
- NetworkAgentInfo nai = getDefaultNetwork();
+ NetworkAgentInfo nai = getFallbackNetwork();
if (nai == null || isNetworkWithCapabilitiesBlocked(nai.networkCapabilities, uid,
ignoreBlocked)) {
return null;
@@ -1636,7 +1638,7 @@
HashMap<Network, NetworkCapabilities> result = new HashMap<>();
- NetworkAgentInfo nai = getDefaultNetwork();
+ final NetworkAgentInfo nai = getFallbackNetwork();
NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
if (nc != null) {
result.put(
@@ -2023,7 +2025,7 @@
// TODO: Move the Dns Event to NetworkMonitor. NetdEventListenerService only allow one
// callback from each caller type. Need to re-factor NetdEventListenerService to allow
// multiple NetworkMonitor registrants.
- if (nai != null && nai.satisfies(mDefaultRequest)) {
+ if (nai != null && nai.satisfies(mFallbackRequest.mRequests.get(0))) {
nai.networkMonitor().notifyDnsResponse(returnCode);
}
}
@@ -2580,12 +2582,12 @@
pw.println();
pw.println();
- final NetworkAgentInfo defaultNai = getDefaultNetwork();
+ final NetworkAgentInfo fallbackNai = getFallbackNetwork();
pw.print("Active default network: ");
- if (defaultNai == null) {
+ if (fallbackNai == null) {
pw.println("none");
} else {
- pw.println(defaultNai.network.getNetId());
+ pw.println(fallbackNai.network.getNetId());
}
pw.println();
@@ -2968,7 +2970,7 @@
final boolean valid = ((testResult & NETWORK_VALIDATION_RESULT_VALID) != 0);
final boolean wasValidated = nai.lastValidated;
- final boolean wasDefault = isDefaultNetwork(nai);
+ final boolean wasFallback = isFallbackNetwork(nai);
if (DBG) {
final String logMsg = !TextUtils.isEmpty(redirectUrl)
@@ -2977,7 +2979,7 @@
log(nai.toShortString() + " validation " + (valid ? "passed" : "failed") + logMsg);
}
if (valid != nai.lastValidated) {
- if (wasDefault) {
+ if (wasFallback) {
mMetricsLog.logDefaultNetworkValidity(valid);
}
final int oldScore = nai.getCurrentScore();
@@ -3353,13 +3355,13 @@
loge("Error connecting NetworkAgent");
mNetworkAgentInfos.remove(nai);
if (nai != null) {
- final boolean wasDefault = isDefaultNetwork(nai);
+ final boolean wasFallback = isFallbackNetwork(nai);
synchronized (mNetworkForNetId) {
mNetworkForNetId.remove(nai.network.getNetId());
}
mNetIdManager.releaseNetId(nai.network.getNetId());
// Just in case.
- mLegacyTypeTracker.remove(nai, wasDefault);
+ mLegacyTypeTracker.remove(nai, wasFallback);
}
}
}
@@ -3398,8 +3400,8 @@
nai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,
null, null);
}
- final boolean wasDefault = isDefaultNetwork(nai);
- if (wasDefault) {
+ final boolean wasFallback = isFallbackNetwork(nai);
+ if (wasFallback) {
mDefaultInetConditionPublished = 0;
// Log default network disconnection before required book-keeping.
// Let rematchAllNetworksAndRequests() below record a new default network event
@@ -3441,19 +3443,24 @@
&& currentNetwork.network.getNetId() == nai.network.getNetId()) {
nri.setSatisfier(null, null);
sendUpdatedScoreToFactories(request, null);
+
+ if (mFallbackRequest == nri) {
+ // TODO : make battery stats aware that since 2013 multiple interfaces may be
+ // active at the same time. For now keep calling this with the fallback
+ // network, because while incorrect this is the closest to the old (also
+ // incorrect) behavior.
+ mNetworkActivityTracker.updateDataActivityTracking(
+ null /* newNetwork */, nai);
+ notifyLockdownVpn(nai);
+ ensureNetworkTransitionWakelock(nai.toShortString());
+ }
}
}
nai.clearLingerState();
- // TODO: this loop, and the mLegacyTypeTracker.remove just below it, seem redundant given
- // there's a full rematch right after. Currently, deleting it breaks tests that check for
- // the default network disconnecting. Find out why, fix the rematch code, and delete this.
- if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
- mDefaultNetworkNai = null;
- mNetworkActivityTracker.updateDataActivityTracking(null /* newNetwork */, nai);
- notifyLockdownVpn(nai);
- ensureNetworkTransitionWakelock(nai.toShortString());
- }
- mLegacyTypeTracker.remove(nai, wasDefault);
+ // TODO: mLegacyTypeTracker.remove seems redundant given there's a full rematch right after.
+ // Currently, deleting it breaks tests that check for the fallback network disconnecting.
+ // Find out why, fix the rematch code, and delete this.
+ mLegacyTypeTracker.remove(nai, wasFallback);
rematchAllNetworksAndRequests();
mLingerMonitor.noteDisconnect(nai);
if (nai.created) {
@@ -4253,7 +4260,7 @@
@Override
public NetworkRequest getDefaultRequest() {
- return mDefaultRequest;
+ return mFallbackRequest.mRequests.get(0);
}
private class InternalHandler extends Handler {
@@ -4499,7 +4506,7 @@
// revalidate the network and generate a ConnectivityDiagnostics ConnectivityReport event.
final NetworkAgentInfo nai;
if (network == null) {
- nai = getDefaultNetwork();
+ nai = getFallbackNetwork();
} else {
nai = getNetworkAgentInfoForNetwork(network);
}
@@ -4518,7 +4525,7 @@
Network network, int uid, boolean hasConnectivity) {
final NetworkAgentInfo nai;
if (network == null) {
- nai = getDefaultNetwork();
+ nai = getFallbackNetwork();
} else {
nai = getNetworkAgentInfoForNetwork(network);
}
@@ -4831,7 +4838,7 @@
}
synchronized (mVpns) {
throwIfLockdownEnabled();
- mVpns.get(user).startLegacyVpn(profile, mKeyStore, egress);
+ mVpns.get(user).startLegacyVpn(profile, mKeyStore, null /* underlying */, egress);
}
}
@@ -4884,7 +4891,7 @@
// see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret
// the underlyingNetworks list.
if (underlyingNetworks == null) {
- NetworkAgentInfo defaultNai = getDefaultNetwork();
+ final NetworkAgentInfo defaultNai = getFallbackNetwork();
if (defaultNai != null) {
underlyingNetworks = new Network[] { defaultNai.network };
}
@@ -4936,7 +4943,7 @@
}
private Network[] underlyingNetworksOrDefault(Network[] underlyingNetworks) {
- final Network defaultNetwork = getNetwork(getDefaultNetwork());
+ final Network defaultNetwork = getNetwork(getFallbackNetwork());
if (underlyingNetworks == null && defaultNetwork != null) {
// null underlying networks means to track the default.
underlyingNetworks = new Network[] { defaultNetwork };
@@ -5493,6 +5500,8 @@
*/
@VisibleForTesting
protected class NetworkRequestInfo implements IBinder.DeathRecipient {
+ // The requests to be satisfied in priority order. Non-multilayer requests will only have a
+ // single NetworkRequest in mRequests.
final List<NetworkRequest> mRequests;
// mSatisfier and mActiveRequest rely on one another therefore set them together.
@@ -6038,11 +6047,13 @@
@GuardedBy("mBlockedAppUids")
private final HashSet<Integer> mBlockedAppUids = new HashSet<>();
+ // The always-on request for an Internet-capable network that apps without a specific default
+ // fall back to.
@NonNull
- private final NetworkRequest mDefaultRequest;
- // The NetworkAgentInfo currently satisfying the default request, if any.
- @Nullable
- private volatile NetworkAgentInfo mDefaultNetworkNai = null;
+ private final NetworkRequestInfo mFallbackRequest;
+ // Collection of NetworkRequestInfo's used for default networks.
+ @NonNull
+ private final ArraySet<NetworkRequestInfo> mDefaultNetworkRequests = new ArraySet<>();
// Request used to optionally keep mobile data active even when higher
// priority networks like Wi-Fi are active.
@@ -6055,8 +6066,10 @@
// Request used to optionally keep vehicle internal network always active
private final NetworkRequest mDefaultVehicleRequest;
- private NetworkAgentInfo getDefaultNetwork() {
- return mDefaultNetworkNai;
+ // TODO: b/178729499 update this in favor of a method taking in a UID.
+ // The NetworkAgentInfo currently satisfying the fallback request, if any.
+ private NetworkAgentInfo getFallbackNetwork() {
+ return mFallbackRequest.mSatisfier;
}
@Nullable
@@ -6073,8 +6086,8 @@
}
@VisibleForTesting
- protected boolean isDefaultNetwork(NetworkAgentInfo nai) {
- return nai == getDefaultNetwork();
+ protected boolean isFallbackNetwork(NetworkAgentInfo nai) {
+ return nai == getFallbackNetwork();
}
// TODO : remove this method. It's a stopgap measure to help sheperding a number of dependent
@@ -6143,8 +6156,8 @@
LinkProperties lp = new LinkProperties(linkProperties);
- // TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
- // satisfies mDefaultRequest.
+ // TODO: Instead of passing mFallbackRequest, provide an API to determine whether a Network
+ // satisfies mFallbackRequest.
final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
final NetworkAgentInfo nai = new NetworkAgentInfo(na,
new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
@@ -6221,7 +6234,7 @@
// for (LinkProperties lp : newLp.getStackedLinks()) {
// updateMtu(lp, null);
// }
- if (isDefaultNetwork(networkAgent)) {
+ if (isFallbackNetwork(networkAgent)) {
updateTcpBufferSizes(newLp.getTcpBufferSizes());
}
@@ -6233,7 +6246,7 @@
// updateDnses will fetch the private DNS configuration from DnsManager.
mDnsManager.updatePrivateDnsStatus(netId, newLp);
- if (isDefaultNetwork(networkAgent)) {
+ if (isFallbackNetwork(networkAgent)) {
handleApplyDefaultProxy(newLp.getHttpProxy());
} else {
updateProxy(newLp, oldLp);
@@ -7184,14 +7197,41 @@
}
}
- private void makeDefault(@Nullable final NetworkAgentInfo newNetwork) {
- if (DBG) log("Switching to new default network: " + newNetwork);
+ private void processDefaultNetworkChanges(@NonNull final NetworkReassignment changes) {
+ boolean isDefaultChanged = false;
+ for (final NetworkRequestInfo defaultRequestInfo : mDefaultNetworkRequests) {
+ final NetworkReassignment.RequestReassignment reassignment =
+ changes.getReassignment(defaultRequestInfo);
+ if (null == reassignment) {
+ continue;
+ }
+ // reassignment only contains those instances where the satisfying network changed.
+ isDefaultChanged = true;
+ // Notify system services of the new default.
+ makeDefault(defaultRequestInfo, reassignment.mOldNetwork, reassignment.mNewNetwork);
+ }
- mDefaultNetworkNai = newNetwork;
+ if (isDefaultChanged) {
+ // Hold a wakelock for a short time to help apps in migrating to a new default.
+ scheduleReleaseNetworkTransitionWakelock();
+ }
+ }
+
+ private void makeDefault(@NonNull final NetworkRequestInfo nri,
+ @Nullable final NetworkAgentInfo oldDefaultNetwork,
+ @Nullable final NetworkAgentInfo newDefaultNetwork) {
+ if (DBG) {
+ log("Switching to new default network for: " + nri + " using " + newDefaultNetwork);
+ }
try {
- if (null != newNetwork) {
- mNetd.networkSetDefault(newNetwork.network.getNetId());
+ // TODO http://b/176191930 update netd calls in follow-up CL for multinetwork changes.
+ if (mFallbackRequest != nri) {
+ return;
+ }
+
+ if (null != newDefaultNetwork) {
+ mNetd.networkSetDefault(newDefaultNetwork.network.getNetId());
} else {
mNetd.networkClearDefault();
}
@@ -7199,16 +7239,41 @@
loge("Exception setting default network :" + e);
}
- notifyLockdownVpn(newNetwork);
- handleApplyDefaultProxy(null != newNetwork
- ? newNetwork.linkProperties.getHttpProxy() : null);
- updateTcpBufferSizes(null != newNetwork
- ? newNetwork.linkProperties.getTcpBufferSizes() : null);
+ if (oldDefaultNetwork != null) {
+ mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork);
+ }
+ mNetworkActivityTracker.updateDataActivityTracking(newDefaultNetwork, oldDefaultNetwork);
+ notifyLockdownVpn(newDefaultNetwork);
+ handleApplyDefaultProxy(null != newDefaultNetwork
+ ? newDefaultNetwork.linkProperties.getHttpProxy() : null);
+ updateTcpBufferSizes(null != newDefaultNetwork
+ ? newDefaultNetwork.linkProperties.getTcpBufferSizes() : null);
notifyIfacesChangedForNetworkStats();
// Fix up the NetworkCapabilities of any networks that have this network as underlying.
- if (newNetwork != null) {
- propagateUnderlyingNetworkCapabilities(newNetwork.network);
+ if (newDefaultNetwork != null) {
+ propagateUnderlyingNetworkCapabilities(newDefaultNetwork.network);
}
+
+ // Log 0 -> X and Y -> X default network transitions, where X is the new default.
+ final Network network = (newDefaultNetwork != null) ? newDefaultNetwork.network : null;
+ final int score = (newDefaultNetwork != null) ? newDefaultNetwork.getCurrentScore() : 0;
+ final boolean validated = newDefaultNetwork != null && newDefaultNetwork.lastValidated;
+ final LinkProperties lp = (newDefaultNetwork != null)
+ ? newDefaultNetwork.linkProperties : null;
+ final NetworkCapabilities nc = (newDefaultNetwork != null)
+ ? newDefaultNetwork.networkCapabilities : null;
+
+ final Network prevNetwork = (oldDefaultNetwork != null)
+ ? oldDefaultNetwork.network : null;
+ final int prevScore = (oldDefaultNetwork != null)
+ ? oldDefaultNetwork.getCurrentScore() : 0;
+ final LinkProperties prevLp = (oldDefaultNetwork != null)
+ ? oldDefaultNetwork.linkProperties : null;
+ final NetworkCapabilities prevNc = (oldDefaultNetwork != null)
+ ? oldDefaultNetwork.networkCapabilities : null;
+
+ mMetricsLog.logDefaultNetworkEvent(network, score, validated, lp, nc,
+ prevNetwork, prevScore, prevLp, prevNc);
}
private void processListenRequests(@NonNull final NetworkAgentInfo nai) {
@@ -7460,46 +7525,8 @@
now);
}
- final NetworkAgentInfo oldDefaultNetwork = getDefaultNetwork();
- final NetworkRequestInfo defaultRequestInfo = mNetworkRequests.get(mDefaultRequest);
- final NetworkReassignment.RequestReassignment reassignment =
- changes.getReassignment(defaultRequestInfo);
- final NetworkAgentInfo newDefaultNetwork =
- null != reassignment ? reassignment.mNewNetwork : oldDefaultNetwork;
-
- if (oldDefaultNetwork != newDefaultNetwork) {
- if (oldDefaultNetwork != null) {
- mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork);
- }
- mNetworkActivityTracker.updateDataActivityTracking(
- newDefaultNetwork, oldDefaultNetwork);
- // Notify system services of the new default.
- makeDefault(newDefaultNetwork);
-
- // Log 0 -> X and Y -> X default network transitions, where X is the new default.
- final Network network = (newDefaultNetwork != null) ? newDefaultNetwork.network : null;
- final int score = (newDefaultNetwork != null) ? newDefaultNetwork.getCurrentScore() : 0;
- final boolean validated = newDefaultNetwork != null && newDefaultNetwork.lastValidated;
- final LinkProperties lp = (newDefaultNetwork != null)
- ? newDefaultNetwork.linkProperties : null;
- final NetworkCapabilities nc = (newDefaultNetwork != null)
- ? newDefaultNetwork.networkCapabilities : null;
-
- final Network prevNetwork = (oldDefaultNetwork != null)
- ? oldDefaultNetwork.network : null;
- final int prevScore = (oldDefaultNetwork != null)
- ? oldDefaultNetwork.getCurrentScore() : 0;
- final LinkProperties prevLp = (oldDefaultNetwork != null)
- ? oldDefaultNetwork.linkProperties : null;
- final NetworkCapabilities prevNc = (oldDefaultNetwork != null)
- ? oldDefaultNetwork.networkCapabilities : null;
-
- mMetricsLog.logDefaultNetworkEvent(network, score, validated, lp, nc,
- prevNetwork, prevScore, prevLp, prevNc);
-
- // Have a new default network, release the transition wakelock in
- scheduleReleaseNetworkTransitionWakelock();
- }
+ // Process default network changes if applicable.
+ processDefaultNetworkChanges(changes);
// Notify requested networks are available after the default net is switched, but
// before LegacyTypeTracker sends legacy broadcasts
@@ -7552,7 +7579,7 @@
notifyNetworkLosing(nai, now);
}
- updateLegacyTypeTrackerAndVpnLockdownForRematch(oldDefaultNetwork, newDefaultNetwork, nais);
+ updateLegacyTypeTrackerAndVpnLockdownForRematch(changes, nais);
// Tear down all unneeded networks.
for (NetworkAgentInfo nai : mNetworkAgentInfos) {
@@ -7595,29 +7622,36 @@
}
private void updateLegacyTypeTrackerAndVpnLockdownForRematch(
- @Nullable final NetworkAgentInfo oldDefaultNetwork,
- @Nullable final NetworkAgentInfo newDefaultNetwork,
+ @NonNull final NetworkReassignment changes,
@NonNull final Collection<NetworkAgentInfo> nais) {
- if (oldDefaultNetwork != newDefaultNetwork) {
+ final NetworkReassignment.RequestReassignment fallbackReassignment =
+ changes.getReassignment(mFallbackRequest);
+ final NetworkAgentInfo oldFallbackNetwork =
+ null != fallbackReassignment ? fallbackReassignment.mOldNetwork : null;
+ final NetworkAgentInfo newFallbackNetwork =
+ null != fallbackReassignment ? fallbackReassignment.mNewNetwork : null;
+
+ if (oldFallbackNetwork != newFallbackNetwork) {
// Maintain the illusion : since the legacy API only understands one network at a time,
// if the default network changed, apps should see a disconnected broadcast for the
// old default network before they see a connected broadcast for the new one.
- if (oldDefaultNetwork != null) {
- mLegacyTypeTracker.remove(oldDefaultNetwork.networkInfo.getType(),
- oldDefaultNetwork, true);
+ if (oldFallbackNetwork != null) {
+ mLegacyTypeTracker.remove(oldFallbackNetwork.networkInfo.getType(),
+ oldFallbackNetwork, true);
}
- if (newDefaultNetwork != null) {
+ if (newFallbackNetwork != null) {
// The new default network can be newly null if and only if the old default
// network doesn't satisfy the default request any more because it lost a
// capability.
- mDefaultInetConditionPublished = newDefaultNetwork.lastValidated ? 100 : 0;
- mLegacyTypeTracker.add(newDefaultNetwork.networkInfo.getType(), newDefaultNetwork);
+ mDefaultInetConditionPublished = newFallbackNetwork.lastValidated ? 100 : 0;
+ mLegacyTypeTracker.add(
+ newFallbackNetwork.networkInfo.getType(), newFallbackNetwork);
// If the legacy VPN is connected, notifyLockdownVpn may end up sending a broadcast
// to reflect the NetworkInfo of this new network. This broadcast has to be sent
// after the disconnect broadcasts above, but before the broadcasts sent by the
// legacy type tracker below.
// TODO : refactor this, it's too complex
- notifyLockdownVpn(newDefaultNetwork);
+ notifyLockdownVpn(newFallbackNetwork);
}
}
@@ -7652,7 +7686,7 @@
}
// A VPN generally won't get added to the legacy tracker in the "for (nri)" loop above,
- // because usually there are no NetworkRequests it satisfies (e.g., mDefaultRequest
+ // because usually there are no NetworkRequests it satisfies (e.g., mFallbackRequest
// wants the NOT_VPN capability, so it will never be satisfied by a VPN). So, add the
// newNetwork to the tracker explicitly (it's a no-op if it has already been added).
if (nai.isVPN()) {
@@ -7663,9 +7697,9 @@
private void updateInetCondition(NetworkAgentInfo nai) {
// Don't bother updating until we've graduated to validated at least once.
if (!nai.everValidated) return;
- // For now only update icons for default connection.
+ // For now only update icons for the fallback connection.
// TODO: Update WiFi and cellular icons separately. b/17237507
- if (!isDefaultNetwork(nai)) return;
+ if (!isFallbackNetwork(nai)) return;
int newInetCondition = nai.lastValidated ? 100 : 0;
// Don't repeat publish.
@@ -7933,8 +7967,8 @@
intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
}
NetworkAgentInfo newDefaultAgent = null;
- if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
- newDefaultAgent = getDefaultNetwork();
+ if (nai.isSatisfyingRequest(mFallbackRequest.mRequests.get(0).requestId)) {
+ newDefaultAgent = getFallbackNetwork();
if (newDefaultAgent != null) {
intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO,
newDefaultAgent.networkInfo);
@@ -7981,10 +8015,10 @@
*/
private Network[] getDefaultNetworks() {
ensureRunningOnConnectivityServiceThread();
- ArrayList<Network> defaultNetworks = new ArrayList<>();
- NetworkAgentInfo defaultNetwork = getDefaultNetwork();
+ final ArrayList<Network> defaultNetworks = new ArrayList<>();
+ final NetworkAgentInfo fallbackNetwork = getFallbackNetwork();
for (NetworkAgentInfo nai : mNetworkAgentInfos) {
- if (nai.everConnected && (nai == defaultNetwork || nai.isVPN())) {
+ if (nai.everConnected && (nai == fallbackNetwork || nai.isVPN())) {
defaultNetworks.add(nai.network);
}
}
@@ -8034,7 +8068,6 @@
int user = UserHandle.getUserId(mDeps.getCallingUid());
final boolean success;
synchronized (mVpns) {
- throwIfLockdownEnabled();
success = mVpns.get(user).setUnderlyingNetworks(networks);
}
return success;
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 8562b0d..6a72010 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -27,10 +27,12 @@
import android.net.ConnectivityManager;
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
+import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.IVcnManagementService;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnUnderlyingNetworkPolicy;
+import android.net.wifi.WifiInfo;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
@@ -291,6 +293,12 @@
@NonNull VcnConfig config) {
return new Vcn(vcnContext, subscriptionGroup, config);
}
+
+ /** Gets the subId indicated by the given {@link WifiInfo}. */
+ public int getSubIdForWifiInfo(@NonNull WifiInfo wifiInfo) {
+ // TODO(b/178501049): use the subId indicated by WifiInfo#getSubscriptionId
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
}
/** Notifies the VcnManagementService that external dependencies can be set up. */
@@ -582,8 +590,36 @@
"Must have permission NETWORK_FACTORY or be the SystemServer to get underlying"
+ " Network policies");
- // TODO(b/175914059): implement policy generation once VcnManagementService is able to
- // determine policies
+ // Defensive copy in case this call is in-process and the given NetworkCapabilities mutates
+ networkCapabilities = new NetworkCapabilities(networkCapabilities);
+
+ int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
+ && networkCapabilities.getNetworkSpecifier() instanceof TelephonyNetworkSpecifier) {
+ TelephonyNetworkSpecifier telephonyNetworkSpecifier =
+ (TelephonyNetworkSpecifier) networkCapabilities.getNetworkSpecifier();
+ subId = telephonyNetworkSpecifier.getSubscriptionId();
+ } else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
+ && networkCapabilities.getTransportInfo() instanceof WifiInfo) {
+ WifiInfo wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
+ subId = mDeps.getSubIdForWifiInfo(wifiInfo);
+ }
+
+ boolean isVcnManagedNetwork = false;
+ if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ synchronized (mLock) {
+ ParcelUuid subGroup = mLastSnapshot.getGroupForSubId(subId);
+
+ // TODO(b/178140910): only mark the Network as VCN-managed if not in safe mode
+ if (mVcns.containsKey(subGroup)) {
+ isVcnManagedNetwork = true;
+ }
+ }
+ }
+ if (isVcnManagedNetwork) {
+ networkCapabilities.removeCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+ }
return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities);
}
diff --git a/services/core/java/com/android/server/VibratorManagerService.java b/services/core/java/com/android/server/VibratorManagerService.java
index 1738c97..6a816af 100644
--- a/services/core/java/com/android/server/VibratorManagerService.java
+++ b/services/core/java/com/android/server/VibratorManagerService.java
@@ -45,10 +45,12 @@
import android.os.VibratorInfo;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.util.DumpUtils;
import com.android.server.vibrator.InputDeviceDelegate;
import com.android.server.vibrator.Vibration;
import com.android.server.vibrator.VibrationScaler;
@@ -61,7 +63,9 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
@@ -106,6 +110,7 @@
private final Handler mHandler;
private final AppOpsManager mAppOps;
private final NativeWrapper mNativeWrapper;
+ private final VibratorManagerRecords mVibratorManagerRecords;
private final int[] mVibratorIds;
private final SparseArray<VibratorController> mVibrators;
private final VibrationCallbacks mVibrationCallbacks = new VibrationCallbacks();
@@ -135,6 +140,10 @@
mNativeWrapper = injector.getNativeWrapper();
mNativeWrapper.init();
+ int dumpLimit = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_previousVibrationsDumpLimit);
+ mVibratorManagerRecords = new VibratorManagerRecords(dumpLimit);
+
mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
BatteryStats.SERVICE_NAME));
@@ -340,7 +349,8 @@
mCurrentVibration.cancel();
}
if (mCurrentExternalVibration != null) {
- // TODO(b/167946816): end vibration and add to list to be dumped for debug
+ mCurrentExternalVibration.end(Vibration.Status.CANCELLED);
+ mVibratorManagerRecords.record(mCurrentExternalVibration);
mCurrentExternalVibration.externalVibration.mute();
mCurrentExternalVibration = null;
// TODO(b/167946816): set external control to false
@@ -355,6 +365,29 @@
}
@Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+
+ final long ident = Binder.clearCallingIdentity();
+
+ boolean isDumpProto = false;
+ for (String arg : args) {
+ if (arg.equals("--proto")) {
+ isDumpProto = true;
+ }
+ }
+ try {
+ if (isDumpProto) {
+ mVibratorManagerRecords.dumpProto(fd);
+ } else {
+ mVibratorManagerRecords.dumpText(pw);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback cb, ResultReceiver resultReceiver) {
new VibratorManagerShellCommand(this).exec(this, in, out, err, args, cb, resultReceiver);
@@ -451,7 +484,8 @@
@GuardedBy("mLock")
private void endVibrationLocked(Vibration vib, Vibration.Status status) {
- // TODO(b/167946816): end vibration and add to list to be dumped for debug
+ vib.end(status);
+ mVibratorManagerRecords.record(vib);
}
@GuardedBy("mLock")
@@ -936,8 +970,158 @@
}
}
+ /** Keep records of vibrations played and provide debug information for this service. */
+ private final class VibratorManagerRecords {
+ @GuardedBy("mLock")
+ private final SparseArray<LinkedList<Vibration.DebugInfo>> mPreviousVibrations =
+ new SparseArray<>();
+ @GuardedBy("mLock")
+ private final LinkedList<Vibration.DebugInfo> mPreviousExternalVibrations =
+ new LinkedList<>();
+ private final int mPreviousVibrationsLimit;
+
+ private VibratorManagerRecords(int limit) {
+ mPreviousVibrationsLimit = limit;
+ }
+
+ @GuardedBy("mLock")
+ void record(Vibration vib) {
+ int usage = vib.attrs.getUsage();
+ if (!mPreviousVibrations.contains(usage)) {
+ mPreviousVibrations.put(usage, new LinkedList<>());
+ }
+ record(mPreviousVibrations.get(usage), vib.getDebugInfo());
+ }
+
+ @GuardedBy("mLock")
+ void record(ExternalVibrationHolder vib) {
+ record(mPreviousExternalVibrations, vib.getDebugInfo());
+ }
+
+ @GuardedBy("mLock")
+ void record(LinkedList<Vibration.DebugInfo> records, Vibration.DebugInfo info) {
+ if (records.size() > mPreviousVibrationsLimit) {
+ records.removeFirst();
+ }
+ records.addLast(info);
+ }
+
+ void dumpText(PrintWriter pw) {
+ pw.println("Vibrator Manager Service:");
+ synchronized (mLock) {
+ pw.println(" mVibratorControllers:");
+ for (int i = 0; i < mVibrators.size(); i++) {
+ pw.println(" " + mVibrators.valueAt(i));
+ }
+ pw.println();
+ pw.println(" mCurrentVibration:");
+ pw.println(" " + mCurrentVibration == null
+ ? null : mCurrentVibration.getVibration().getDebugInfo());
+ pw.println(" mNextVibration:");
+ pw.println(" " + mNextVibration == null
+ ? null : mNextVibration.getVibration().getDebugInfo());
+ pw.println(" mCurrentExternalVibration:");
+ pw.println(" " + mCurrentExternalVibration == null
+ ? null : mCurrentExternalVibration.getDebugInfo());
+ pw.println();
+ pw.println(" mVibrationSettings=" + mVibrationSettings);
+ for (int i = 0; i < mPreviousVibrations.size(); i++) {
+ pw.println();
+ pw.print(" Previous vibrations for usage ");
+ pw.print(VibrationAttributes.usageToString(mPreviousVibrations.keyAt(i)));
+ pw.println(":");
+ for (Vibration.DebugInfo info : mPreviousVibrations.valueAt(i)) {
+ pw.println(" " + info);
+ }
+ }
+
+ pw.println(" Previous external vibrations:");
+ for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
+ pw.println(" " + info);
+ }
+ }
+ }
+
+ synchronized void dumpProto(FileDescriptor fd) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+
+ synchronized (mLock) {
+ mVibrationSettings.dumpProto(proto);
+ if (mCurrentVibration != null) {
+ mCurrentVibration.getVibration().getDebugInfo().dumpProto(proto,
+ VibratorServiceDumpProto.CURRENT_VIBRATION);
+ }
+ if (mCurrentExternalVibration != null) {
+ mCurrentExternalVibration.getDebugInfo().dumpProto(proto,
+ VibratorServiceDumpProto.CURRENT_EXTERNAL_VIBRATION);
+ }
+
+ boolean isVibrating = false;
+ boolean isUnderExternalControl = false;
+ for (int i = 0; i < mVibrators.size(); i++) {
+ isVibrating |= mVibrators.valueAt(i).isVibrating();
+ isUnderExternalControl |= mVibrators.valueAt(i).isUnderExternalControl();
+ }
+ proto.write(VibratorServiceDumpProto.IS_VIBRATING, isVibrating);
+ proto.write(VibratorServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
+ isUnderExternalControl);
+
+ for (Vibration.DebugInfo info : mPreviousVibrations.get(
+ VibrationAttributes.USAGE_RINGTONE)) {
+ info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_RING_VIBRATIONS);
+ }
+
+ for (Vibration.DebugInfo info : mPreviousVibrations.get(
+ VibrationAttributes.USAGE_NOTIFICATION)) {
+ info.dumpProto(proto,
+ VibratorServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS);
+ }
+
+ for (Vibration.DebugInfo info : mPreviousVibrations.get(
+ VibrationAttributes.USAGE_ALARM)) {
+ info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS);
+ }
+
+ for (Vibration.DebugInfo info : mPreviousVibrations.get(
+ VibrationAttributes.USAGE_UNKNOWN)) {
+ info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_VIBRATIONS);
+ }
+
+ for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
+ info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS);
+ }
+ }
+ proto.flush();
+ }
+ }
+
/** Provide limited functionality from {@link VibratorManagerService} as shell commands. */
private final class VibratorManagerShellCommand extends ShellCommand {
+ public static final String SHELL_PACKAGE_NAME = "com.android.shell";
+
+ private final class CommonOptions {
+ public boolean force = false;
+ public String description = "Shell command";
+
+ CommonOptions() {
+ String nextArg;
+ while ((nextArg = peekNextArg()) != null) {
+ switch (nextArg) {
+ case "-f":
+ getNextArgRequired(); // consume the -f argument;
+ force = true;
+ break;
+ case "-d":
+ getNextArgRequired(); // consume the -d argument;
+ description = getNextArgRequired();
+ break;
+ default:
+ // Not a common option, finish reading.
+ return;
+ }
+ }
+ }
+ }
private final IBinder mToken;
@@ -947,10 +1131,27 @@
@Override
public int onCommand(String cmd) {
- if ("list".equals(cmd)) {
- return runListVibrators();
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "onCommand " + cmd);
+ try {
+ if ("list".equals(cmd)) {
+ return runListVibrators();
+ }
+ if ("synced".equals(cmd)) {
+ return runMono();
+ }
+ if ("combined".equals(cmd)) {
+ return runStereo();
+ }
+ if ("sequential".equals(cmd)) {
+ return runSequential();
+ }
+ if ("cancel".equals(cmd)) {
+ return runCancel();
+ }
+ return handleDefaultCommands(cmd);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
- return handleDefaultCommands(cmd);
}
private int runListVibrators() {
@@ -967,6 +1168,157 @@
}
}
+ private int runMono() {
+ CommonOptions commonOptions = new CommonOptions();
+ VibrationEffect effect = nextEffect();
+ if (effect == null) {
+ return 0;
+ }
+
+ CombinedVibrationEffect combinedEffect = CombinedVibrationEffect.createSynced(effect);
+ VibrationAttributes attrs = createVibrationAttributes(commonOptions);
+ vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, combinedEffect, attrs,
+ commonOptions.description, mToken);
+ return 0;
+ }
+
+ private int runStereo() {
+ CommonOptions commonOptions = new CommonOptions();
+ CombinedVibrationEffect.SyncedCombination combination =
+ CombinedVibrationEffect.startSynced();
+ while ("-v".equals(getNextOption())) {
+ int vibratorId = Integer.parseInt(getNextArgRequired());
+ VibrationEffect effect = nextEffect();
+ if (effect != null) {
+ combination.addVibrator(vibratorId, effect);
+ }
+ }
+ VibrationAttributes attrs = createVibrationAttributes(commonOptions);
+ vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, combination.combine(), attrs,
+ commonOptions.description, mToken);
+ return 0;
+ }
+
+ private int runSequential() {
+ CommonOptions commonOptions = new CommonOptions();
+
+ CombinedVibrationEffect.SequentialCombination combination =
+ CombinedVibrationEffect.startSequential();
+ while ("-v".equals(getNextOption())) {
+ int vibratorId = Integer.parseInt(getNextArgRequired());
+ int delay = 0;
+ if ("-w".equals(getNextOption())) {
+ delay = Integer.parseInt(getNextArgRequired());
+ }
+ VibrationEffect effect = nextEffect();
+ if (effect != null) {
+ combination.addNext(vibratorId, effect, delay);
+ }
+ }
+ VibrationAttributes attrs = createVibrationAttributes(commonOptions);
+ vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, combination.combine(), attrs,
+ commonOptions.description, mToken);
+ return 0;
+ }
+
+ private int runCancel() {
+ cancelVibrate(mToken);
+ return 0;
+ }
+
+ @Nullable
+ private VibrationEffect nextEffect() {
+ String effectType = getNextArgRequired();
+ if ("oneshot".equals(effectType)) {
+ return nextOneShot();
+ }
+ if ("waveform".equals(effectType)) {
+ return nextWaveform();
+ }
+ if ("prebaked".equals(effectType)) {
+ return nextPrebaked();
+ }
+ if ("composed".equals(effectType)) {
+ return nextComposed();
+ }
+ return null;
+ }
+
+ private VibrationEffect nextOneShot() {
+ boolean hasAmplitude = "-a".equals(getNextOption());
+ long duration = Long.parseLong(getNextArgRequired());
+ int amplitude = hasAmplitude ? Integer.parseInt(getNextArgRequired())
+ : VibrationEffect.DEFAULT_AMPLITUDE;
+ return VibrationEffect.createOneShot(duration, amplitude);
+ }
+
+ private VibrationEffect nextWaveform() {
+ boolean hasAmplitudes = false;
+ int repeat = -1;
+
+ String nextOption = getNextOption();
+ while (nextOption != null) {
+ if ("-a".equals(nextOption)) {
+ hasAmplitudes = true;
+ } else if ("-r".equals(nextOption)) {
+ repeat = Integer.parseInt(getNextArgRequired());
+ }
+ nextOption = getNextOption();
+ }
+ List<Long> durations = new ArrayList<>();
+ List<Integer> amplitudes = new ArrayList<>();
+
+ String nextArg;
+ while ((nextArg = peekNextArg()) != null && !"-v".equals(nextArg)) {
+ durations.add(Long.parseLong(getNextArgRequired()));
+ if (hasAmplitudes) {
+ amplitudes.add(Integer.parseInt(getNextArgRequired()));
+ }
+ }
+
+ long[] durationArray = durations.stream().mapToLong(Long::longValue).toArray();
+ if (!hasAmplitudes) {
+ return VibrationEffect.createWaveform(durationArray, repeat);
+ }
+
+ int[] amplitudeArray = amplitudes.stream().mapToInt(Integer::intValue).toArray();
+ return VibrationEffect.createWaveform(durationArray, amplitudeArray, repeat);
+ }
+
+ private VibrationEffect nextPrebaked() {
+ boolean shouldFallback = "-b".equals(getNextOption());
+ int effectId = Integer.parseInt(getNextArgRequired());
+ return VibrationEffect.get(effectId, shouldFallback);
+ }
+
+ private VibrationEffect nextComposed() {
+ VibrationEffect.Composition composition = VibrationEffect.startComposition();
+ String nextArg;
+ while ((nextArg = peekNextArg()) != null) {
+ int delay = 0;
+ if ("-w".equals(nextArg)) {
+ getNextArgRequired(); // consume the -w option
+ delay = Integer.parseInt(getNextArgRequired());
+ } else if ("-v".equals(nextArg)) {
+ // Starting next vibrator, this composed effect if finished.
+ break;
+ }
+ int primitiveId = Integer.parseInt(getNextArgRequired());
+ composition.addPrimitive(primitiveId, /* scale= */ 1f, delay);
+ }
+ return composition.compose();
+ }
+
+ private VibrationAttributes createVibrationAttributes(CommonOptions commonOptions) {
+ final int flags =
+ commonOptions.force ? VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY : 0;
+ return new VibrationAttributes.Builder()
+ .setFlags(flags, VibrationAttributes.FLAG_ALL_SUPPORTED)
+ // Used to apply Settings.System.HAPTIC_FEEDBACK_INTENSITY to scale effects.
+ .setUsage(VibrationAttributes.USAGE_TOUCH)
+ .build();
+ }
+
@Override
public void onHelp() {
try (PrintWriter pw = getOutPrintWriter();) {
@@ -977,6 +1329,49 @@
pw.println(" list");
pw.println(" Prints the id of device vibrators. This does not include any ");
pw.println(" connected input device.");
+ pw.println(" synced [options] <effect>");
+ pw.println(" Vibrates effect on all vibrators in sync.");
+ pw.println(" combined [options] (-v <vibrator-id> <effect>)...");
+ pw.println(" Vibrates different effects on each vibrator in sync.");
+ pw.println(" sequential [options] (-v <vibrator-id> [-w <delay>] <effect>)...");
+ pw.println(" Vibrates different effects on each vibrator in sequence.");
+ pw.println(" cancel");
+ pw.println(" Cancels any active vibration");
+ pw.println("");
+ pw.println("Effect commands:");
+ pw.println(" oneshot [-a] <duration> [<amplitude>]");
+ pw.println(" Vibrates for duration milliseconds; ignored when device is on ");
+ pw.println(" DND (Do Not Disturb) mode; touch feedback strength user setting ");
+ pw.println(" will be used to scale amplitude.");
+ pw.println(" If -a is provided, the command accepts a second argument for ");
+ pw.println(" amplitude, in a scale of 1-255.");
+ pw.println(" waveform [-r <index>] [-a] (<duration> [<amplitude>])...");
+ pw.println(" Vibrates for durations and amplitudes in list; ignored when ");
+ pw.println(" device is on DND (Do Not Disturb) mode; touch feedback strength ");
+ pw.println(" user setting will be used to scale amplitude.");
+ pw.println(" If -r is provided, the waveform loops back to the specified");
+ pw.println(" index (e.g. 0 loops from the beginning)");
+ pw.println(" If -a is provided, the command accepts duration-amplitude pairs;");
+ pw.println(" otherwise, it accepts durations only and alternates off/on");
+ pw.println(" Duration is in milliseconds; amplitude is a scale of 1-255.");
+ pw.println(" prebaked [-b] <effect-id>");
+ pw.println(" Vibrates with prebaked effect; ignored when device is on DND ");
+ pw.println(" (Do Not Disturb) mode; touch feedback strength user setting ");
+ pw.println(" will be used to scale amplitude.");
+ pw.println(" If -b is provided, the prebaked fallback effect will be played if");
+ pw.println(" the device doesn't support the given effect-id.");
+ pw.println(" composed [-w <delay>] <primitive-id>...");
+ pw.println(" Vibrates with a composed effect; ignored when device is on DND ");
+ pw.println(" (Do Not Disturb) mode; touch feedback strength user setting ");
+ pw.println(" will be used to scale primitive intensities.");
+ pw.println(" If -w is provided, the next primitive will be played after the ");
+ pw.println(" specified wait time in milliseconds.");
+ pw.println("");
+ pw.println("Common Options:");
+ pw.println(" -f");
+ pw.println(" Force. Ignore Do Not Disturb setting.");
+ pw.println(" -d <description>");
+ pw.println(" Add description to the vibration.");
pw.println("");
}
}
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 97e313e..026eb63 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -772,18 +772,7 @@
proto.write(VibratorServiceDumpProto.IS_VIBRATING, mVibratorController.isVibrating());
proto.write(VibratorServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
mVibratorController.isUnderExternalControl());
- proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY,
- mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_TOUCH));
- proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_DEFAULT_INTENSITY,
- mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_TOUCH));
- proto.write(VibratorServiceDumpProto.NOTIFICATION_INTENSITY,
- mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_NOTIFICATION));
- proto.write(VibratorServiceDumpProto.NOTIFICATION_DEFAULT_INTENSITY,
- mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_NOTIFICATION));
- proto.write(VibratorServiceDumpProto.RING_INTENSITY,
- mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE));
- proto.write(VibratorServiceDumpProto.RING_DEFAULT_INTENSITY,
- mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_RINGTONE));
+ mVibrationSettings.dumpProto(proto);
for (Vibration.DebugInfo info : mPreviousRingVibrations) {
info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_RING_VIBRATIONS);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index d3b5b5a..303c080 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -114,11 +114,8 @@
* Requests for the authenticatorId (whose source of truth is in the TEE or equivalent) to
* be invalidated. See {@link com.android.server.biometrics.sensors.InvalidationRequesterClient}
*/
- default void scheduleInvalidateAuthenticatorId(int sensorId, int userId,
- @NonNull IInvalidationCallback callback) {
- throw new IllegalStateException("Providers that support invalidation must override"
- + " this method");
- }
+ void scheduleInvalidateAuthenticatorId(int sensorId, int userId,
+ @NonNull IInvalidationCallback callback);
long getAuthenticatorId(int sensorId, int userId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index a4a8401..9869f77 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -28,6 +28,7 @@
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
@@ -144,11 +145,11 @@
private final LockoutFrameworkImpl.LockoutResetCallback mLockoutResetCallback =
new LockoutFrameworkImpl.LockoutResetCallback() {
- @Override
- public void onLockoutReset(int userId) {
- mLockoutResetDispatcher.notifyLockoutResetCallbacks(mSensorProperties.sensorId);
- }
- };
+ @Override
+ public void onLockoutReset(int userId) {
+ mLockoutResetDispatcher.notifyLockoutResetCallbacks(mSensorProperties.sensorId);
+ }
+ };
private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
@Override
@@ -355,7 +356,7 @@
final int maxEnrollmentsPerUser = mContext.getResources()
.getInteger(R.integer.config_fingerprintMaxTemplatesPerUser);
- mSensorProperties = new FingerprintSensorPropertiesInternal(sensorId,
+ mSensorProperties = new FingerprintSensorPropertiesInternal(context, sensorId,
Utils.authenticatorStrengthToPropertyStrength(strength), maxEnrollmentsPerUser,
sensorType, resetLockoutRequiresHardwareAuthToken);
}
@@ -630,13 +631,13 @@
@NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
@NonNull String opPackageName) {
mHandler.post(() -> {
- scheduleUpdateActiveUserWithoutHandler(userId);
+ scheduleUpdateActiveUserWithoutHandler(userId);
- final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
- mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId,
- userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
- mSensorProperties.sensorId, mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client);
+ final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId,
+ userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
+ mSensorProperties.sensorId, mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client);
});
}
@@ -780,6 +781,17 @@
}
@Override
+ public void scheduleInvalidateAuthenticatorId(int sensorId, int userId,
+ @NonNull IInvalidationCallback callback) {
+ // TODO (b/179101888): Remove this temporary workaround.
+ try {
+ callback.onCompleted();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to complete InvalidateAuthenticatorId");
+ }
+ }
+
+ @Override
public void dumpInternal(int sensorId, @NonNull PrintWriter pw) {
PerformanceTracker performanceTracker =
PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId);
diff --git a/services/core/java/com/android/server/connectivity/PacProxyInstaller.java b/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
index 5dc8c1a..aadaf4d 100644
--- a/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
+++ b/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
@@ -16,7 +16,6 @@
package com.android.server.connectivity;
-import android.annotation.NonNull;
import android.annotation.WorkerThread;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -72,6 +71,10 @@
private static final int DELAY_LONG = 4;
private static final long MAX_PAC_SIZE = 20 * 1000 * 1000;
+ // Return values for #setCurrentProxyScriptUrl
+ public static final boolean DONT_SEND_BROADCAST = false;
+ public static final boolean DO_SEND_BROADCAST = true;
+
private String mCurrentPac;
@GuardedBy("mProxyLock")
private volatile Uri mPacUrl = Uri.EMPTY;
@@ -90,7 +93,7 @@
private volatile boolean mHasSentBroadcast;
private volatile boolean mHasDownloaded;
- private final Handler mConnectivityHandler;
+ private Handler mConnectivityHandler;
private final int mProxyMessage;
/**
@@ -99,13 +102,6 @@
private final Object mProxyLock = new Object();
/**
- * Lock ensuring consistency between the values of mHasSentBroadcast, mHasDownloaded, the
- * last URL and port, and the broadcast message being sent with the correct arguments.
- * TODO : this should probably protect all instances of these variables
- */
- private final Object mBroadcastStateLock = new Object();
-
- /**
* Runnable to download PAC script.
* The behavior relies on the assumption it always runs on mNetThread to guarantee that the
* latest data fetched from mPacUrl is stored in mProxyService.
@@ -150,7 +146,7 @@
}
}
- public PacProxyInstaller(@NonNull Context context, @NonNull Handler handler, int proxyMessage) {
+ public PacProxyInstaller(Context context, Handler handler, int proxyMessage) {
mContext = context;
mLastPort = -1;
final HandlerThread netThread = new HandlerThread("android.pacproxyinstaller",
@@ -180,27 +176,31 @@
* PacProxyInstaller will trigger a new broadcast when it is ready.
*
* @param proxy Proxy information that is about to be broadcast.
+ * @return Returns whether the broadcast should be sent : either DO_ or DONT_SEND_BROADCAST
*/
- public void setCurrentProxyScriptUrl(@NonNull ProxyInfo proxy) {
- synchronized (mBroadcastStateLock) {
- if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
- if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) return;
- mPacUrl = proxy.getPacFileUrl();
- mCurrentDelay = DELAY_1;
- mHasSentBroadcast = false;
- mHasDownloaded = false;
- getAlarmManager().cancel(mPacRefreshIntent);
- bind();
- } else {
- getAlarmManager().cancel(mPacRefreshIntent);
- synchronized (mProxyLock) {
- mPacUrl = Uri.EMPTY;
- mCurrentPac = null;
- if (mProxyService != null) {
- unbind();
- }
+ public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
+ if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
+ if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
+ // Allow to send broadcast, nothing to do.
+ return DO_SEND_BROADCAST;
+ }
+ mPacUrl = proxy.getPacFileUrl();
+ mCurrentDelay = DELAY_1;
+ mHasSentBroadcast = false;
+ mHasDownloaded = false;
+ getAlarmManager().cancel(mPacRefreshIntent);
+ bind();
+ return DONT_SEND_BROADCAST;
+ } else {
+ getAlarmManager().cancel(mPacRefreshIntent);
+ synchronized (mProxyLock) {
+ mPacUrl = Uri.EMPTY;
+ mCurrentPac = null;
+ if (mProxyService != null) {
+ unbind();
}
}
+ return DO_SEND_BROADCAST;
}
}
@@ -275,7 +275,6 @@
getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent);
}
- @GuardedBy("mProxyLock")
private void setCurrentProxyScript(String script) {
if (mProxyService == null) {
Log.e(TAG, "setCurrentProxyScript: no proxy service");
@@ -348,9 +347,6 @@
public void setProxyPort(int port) {
if (mLastPort != -1) {
// Always need to send if port changed
- // TODO: Here lacks synchronization because this write cannot
- // guarantee that it's visible from sendProxyIfNeeded() when
- // it's called by a Runnable which is post by mNetThread.
mHasSentBroadcast = false;
}
mLastPort = port;
@@ -390,15 +386,13 @@
mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
}
- private void sendProxyIfNeeded() {
- synchronized (mBroadcastStateLock) {
- if (!mHasDownloaded || (mLastPort == -1)) {
- return;
- }
- if (!mHasSentBroadcast) {
- sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort));
- mHasSentBroadcast = true;
- }
+ private synchronized void sendProxyIfNeeded() {
+ if (!mHasDownloaded || (mLastPort == -1)) {
+ return;
+ }
+ if (!mHasSentBroadcast) {
+ sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort));
+ mHasSentBroadcast = true;
}
}
}
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index b618d2b..d83ff83 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -226,9 +226,9 @@
final ProxyInfo defaultProxy = getDefaultProxy();
final ProxyInfo proxyInfo = null != defaultProxy ?
defaultProxy : ProxyInfo.buildDirectProxy("", 0, Collections.emptyList());
- mPacProxyInstaller.setCurrentProxyScriptUrl(proxyInfo);
- if (!shouldSendBroadcast(proxyInfo)) {
+ if (mPacProxyInstaller.setCurrentProxyScriptUrl(proxyInfo)
+ == PacProxyInstaller.DONT_SEND_BROADCAST) {
return;
}
if (DBG) Log.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
@@ -244,13 +244,6 @@
}
}
- private boolean shouldSendBroadcast(ProxyInfo proxy) {
- if (Uri.EMPTY.equals(proxy.getPacFileUrl())) return false;
- if (proxy.getPacFileUrl().equals(proxy.getPacFileUrl())
- && (proxy.getPort() > 0)) return true;
- return true;
- }
-
/**
* Sets the global proxy in memory. Also writes the values to the global settings of the device.
*
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index b455a3f..d956ba3 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1982,27 +1982,28 @@
* secondary thread to perform connection work, returning quickly.
*
* Should only be called to respond to Binder requests as this enforces caller permission. Use
- * {@link #startLegacyVpnPrivileged(VpnProfile, KeyStore, LinkProperties)} to skip the
+ * {@link #startLegacyVpnPrivileged(VpnProfile, KeyStore, Network, LinkProperties)} to skip the
* permission check only when the caller is trusted (or the call is initiated by the system).
*/
- public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
+ public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, @Nullable Network underlying,
+ LinkProperties egress) {
enforceControlPermission();
final long token = Binder.clearCallingIdentity();
try {
- startLegacyVpnPrivileged(profile, keyStore, egress);
+ startLegacyVpnPrivileged(profile, keyStore, underlying, egress);
} finally {
Binder.restoreCallingIdentity(token);
}
}
/**
- * Like {@link #startLegacyVpn(VpnProfile, KeyStore, LinkProperties)}, but does not check
- * permissions under the assumption that the caller is the system.
+ * Like {@link #startLegacyVpn(VpnProfile, KeyStore, Network, LinkProperties)}, but does not
+ * check permissions under the assumption that the caller is the system.
*
* Callers are responsible for checking permissions if needed.
*/
public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore,
- LinkProperties egress) {
+ @Nullable Network underlying, @NonNull LinkProperties egress) {
UserManager mgr = UserManager.get(mContext);
UserInfo user = mgr.getUserInfo(mUserId);
if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
@@ -2128,6 +2129,9 @@
config.session = profile.name;
config.isMetered = false;
config.proxyInfo = profile.proxy;
+ if (underlying != null) {
+ config.underlyingNetworks = new Network[] { underlying };
+ }
config.addLegacyRoutes(profile.routes);
if (!profile.dnsServers.isEmpty()) {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 2641ee7..bf16a6d 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -23,6 +23,7 @@
import android.view.DisplayAddress;
import android.view.DisplayCutout;
import android.view.DisplayEventReceiver;
+import android.view.RoundedCorners;
import android.view.Surface;
import com.android.internal.BrightnessSynchronizer;
@@ -281,6 +282,11 @@
public DisplayCutout displayCutout;
/**
+ * The {@link RoundedCorners} if present or {@code null} otherwise.
+ */
+ public RoundedCorners roundedCorners;
+
+ /**
* The touch attachment, per {@link DisplayViewport#touch}.
*/
public int touch;
@@ -401,7 +407,8 @@
|| !BrightnessSynchronizer.floatEquals(brightnessMinimum, other.brightnessMinimum)
|| !BrightnessSynchronizer.floatEquals(brightnessMaximum, other.brightnessMaximum)
|| !BrightnessSynchronizer.floatEquals(brightnessDefault,
- other.brightnessDefault)) {
+ other.brightnessDefault)
+ || !Objects.equals(roundedCorners, other.roundedCorners)) {
diff |= DIFF_OTHER;
}
return diff;
@@ -444,6 +451,7 @@
brightnessMinimum = other.brightnessMinimum;
brightnessMaximum = other.brightnessMaximum;
brightnessDefault = other.brightnessDefault;
+ roundedCorners = other.roundedCorners;
}
// For debugging purposes
@@ -487,6 +495,9 @@
sb.append(", brightnessMinimum ").append(brightnessMinimum);
sb.append(", brightnessMaximum ").append(brightnessMaximum);
sb.append(", brightnessDefault ").append(brightnessDefault);
+ if (roundedCorners != null) {
+ sb.append(", roundedCorners ").append(roundedCorners);
+ }
sb.append(flagsToString(flags));
sb.append("}");
return sb.toString();
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/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 8198e51..7e6a137 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -35,6 +35,7 @@
import android.view.DisplayAddress;
import android.view.DisplayCutout;
import android.view.DisplayEventReceiver;
+import android.view.RoundedCorners;
import android.view.SurfaceControl;
import com.android.internal.BrightnessSynchronizer;
@@ -604,6 +605,8 @@
}
mInfo.displayCutout = DisplayCutout.fromResourcesRectApproximation(res,
mInfo.width, mInfo.height);
+ mInfo.roundedCorners = RoundedCorners.fromResources(
+ res, mInfo.width, mInfo.height);
} else {
if (!res.getBoolean(
com.android.internal.R.bool.config_localDisplaysMirrorContent)) {
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 86de159..80781d2 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -195,6 +195,7 @@
info.logicalDensityDpi = mOverrideDisplayInfo.logicalDensityDpi;
info.physicalXDpi = mOverrideDisplayInfo.physicalXDpi;
info.physicalYDpi = mOverrideDisplayInfo.physicalYDpi;
+ info.roundedCorners = mOverrideDisplayInfo.roundedCorners;
}
mInfo.set(info);
}
@@ -386,6 +387,7 @@
mBaseDisplayInfo.brightnessMinimum = deviceInfo.brightnessMinimum;
mBaseDisplayInfo.brightnessMaximum = deviceInfo.brightnessMaximum;
mBaseDisplayInfo.brightnessDefault = deviceInfo.brightnessDefault;
+ mBaseDisplayInfo.roundedCorners = deviceInfo.roundedCorners;
mPrimaryDisplayDeviceInfo = deviceInfo;
mInfo.set(null);
}
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/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index ea2788c..6d1c680 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -155,7 +155,7 @@
try {
// Use the privileged method because Lockdown VPN is initiated by the system, so
// no additional permission checks are necessary.
- mVpn.startLegacyVpnPrivileged(mProfile, mKeyStore, egressProp);
+ mVpn.startLegacyVpnPrivileged(mProfile, mKeyStore, null, egressProp);
} catch (IllegalStateException e) {
mAcceptedEgressIface = null;
Log.e(TAG, "Failed to start VPN", e);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 29eaf4f..504eefe 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -243,6 +243,7 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.StatLogger;
import com.android.internal.util.XmlUtils;
+import com.android.net.module.util.NetworkIdentityUtils;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
@@ -2163,13 +2164,14 @@
if (template.matches(probeIdent)) {
if (LOGD) {
Slog.d(TAG, "Found template " + template + " which matches subscriber "
- + NetworkIdentity.scrubSubscriberId(subscriberId));
+ + NetworkIdentityUtils.scrubSubscriberId(subscriberId));
}
return false;
}
}
- Slog.i(TAG, "No policy for subscriber " + NetworkIdentity.scrubSubscriberId(subscriberId)
+ Slog.i(TAG, "No policy for subscriber "
+ + NetworkIdentityUtils.scrubSubscriberId(subscriberId)
+ "; generating default policy");
final NetworkPolicy policy = buildDefaultMobilePolicy(subId, subscriberId);
addNetworkPolicyAL(policy);
@@ -3614,14 +3616,15 @@
final int subId = mSubIdToSubscriberId.keyAt(i);
final String subscriberId = mSubIdToSubscriberId.valueAt(i);
- fout.println(subId + "=" + NetworkIdentity.scrubSubscriberId(subscriberId));
+ fout.println(subId + "="
+ + NetworkIdentityUtils.scrubSubscriberId(subscriberId));
}
fout.decreaseIndent();
fout.println();
for (String[] mergedSubscribers : mMergedSubscriberIds) {
fout.println("Merged subscriptions: " + Arrays.toString(
- NetworkIdentity.scrubSubscriberId(mergedSubscribers)));
+ NetworkIdentityUtils.scrubSubscriberIds(mergedSubscribers)));
}
fout.println();
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/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index f8990c0..5d7c41c 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -588,29 +588,32 @@
*
* @param recipientUid the uid gaining visibility of the {@code visibleUid}.
* @param visibleUid the uid becoming visible to the {@recipientUid}
+ * @return {@code true} if implicit access was not already granted.
*/
- public void grantImplicitAccess(int recipientUid, int visibleUid) {
- if (recipientUid != visibleUid) {
- final boolean changed = mImplicitlyQueryable.add(recipientUid, visibleUid);
- if (changed && DEBUG_LOGGING) {
- Slog.i(TAG, "implicit access granted: " + recipientUid + " -> " + visibleUid);
- }
- synchronized (mCacheLock) {
- if (mShouldFilterCache != null) {
- // update the cache in a one-off manner since we've got all the information we
- // need.
- SparseBooleanArray visibleUids = mShouldFilterCache.get(recipientUid);
- if (visibleUids == null) {
- visibleUids = new SparseBooleanArray();
- mShouldFilterCache.put(recipientUid, visibleUids);
- }
- visibleUids.put(visibleUid, false);
+ public boolean grantImplicitAccess(int recipientUid, int visibleUid) {
+ if (recipientUid == visibleUid) {
+ return false;
+ }
+ final boolean changed = mImplicitlyQueryable.add(recipientUid, visibleUid);
+ if (changed && DEBUG_LOGGING) {
+ Slog.i(TAG, "implicit access granted: " + recipientUid + " -> " + visibleUid);
+ }
+ synchronized (mCacheLock) {
+ if (mShouldFilterCache != null) {
+ // update the cache in a one-off manner since we've got all the information we
+ // need.
+ SparseBooleanArray visibleUids = mShouldFilterCache.get(recipientUid);
+ if (visibleUids == null) {
+ visibleUids = new SparseBooleanArray();
+ mShouldFilterCache.put(recipientUid, visibleUids);
}
- }
- if (changed) {
- onChanged();
+ visibleUids.put(visibleUid, false);
}
}
+ if (changed) {
+ onChanged();
+ }
+ return changed;
}
public void onSystemReady() {
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index c3bca28..15e1d52 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -470,23 +470,34 @@
return instantGrantList.get(instantAppId);
}
+ /**
+ * Allows an app to see an instant app.
+ *
+ * @param userId the userId in which this access is being granted
+ * @param intent when provided, this serves as the intent that caused
+ * this access to be granted
+ * @param recipientUid the uid of the app receiving visibility
+ * @param instantAppId the app ID of the instant app being made visible
+ * to the recipient
+ * @return {@code true} if access is granted.
+ */
@GuardedBy("mService.mLock")
- public void grantInstantAccessLPw(@UserIdInt int userId, @Nullable Intent intent,
+ public boolean grantInstantAccessLPw(@UserIdInt int userId, @Nullable Intent intent,
int recipientUid, int instantAppId) {
if (mInstalledInstantAppUids == null) {
- return; // no instant apps installed; no need to grant
+ return false; // no instant apps installed; no need to grant
}
WatchedSparseBooleanArray instantAppList = mInstalledInstantAppUids.get(userId);
if (instantAppList == null || !instantAppList.get(instantAppId)) {
- return; // instant app id isn't installed; no need to grant
+ return false; // instant app id isn't installed; no need to grant
}
if (instantAppList.get(recipientUid)) {
- return; // target app id is an instant app; no need to grant
+ return false; // target app id is an instant app; no need to grant
}
if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) {
final Set<String> categories = intent.getCategories();
if (categories != null && categories.contains(Intent.CATEGORY_BROWSABLE)) {
- return; // launched via VIEW/BROWSABLE intent; no need to grant
+ return false; // launched via VIEW/BROWSABLE intent; no need to grant
}
}
WatchedSparseArray<WatchedSparseBooleanArray> targetAppList = mInstantGrants.get(userId);
@@ -500,6 +511,7 @@
targetAppList.put(recipientUid, instantGrantList);
}
instantGrantList.put(instantAppId, true /*granted*/);
+ return true;
}
@GuardedBy("mService.mLock")
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f68113d..6e5bd94 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3863,7 +3863,8 @@
int i = 0;
for (int index = 0; index < N; index++) {
final PackageSetting ps = sus.packages.valueAt(index);
- if (ps.getInstalled(userId)) {
+ if (ps.getInstalled(userId)
+ && !shouldFilterApplicationLocked(ps, callingUid, userId)) {
res[i++] = ps.name;
}
}
@@ -27187,6 +27188,7 @@
final boolean instantApp =
isInstantAppInternal(visiblePackage.getPackageName(), userId, visibleUid);
+ final boolean accessGranted;
if (instantApp) {
if (!direct) {
// if the interaction that lead to this granting access to an instant app
@@ -27194,10 +27196,13 @@
// grant.
return;
}
- mInstantAppRegistry.grantInstantAccessLPw(userId, intent,
+ accessGranted = mInstantAppRegistry.grantInstantAccessLPw(userId, intent,
recipientAppId, UserHandle.getAppId(visibleUid) /*instantAppId*/);
} else {
- mAppsFilter.grantImplicitAccess(recipientUid, visibleUid);
+ accessGranted = mAppsFilter.grantImplicitAccess(recipientUid, visibleUid);
+ }
+ if (accessGranted) {
+ ApplicationPackageManager.invalidateGetPackagesForUidCache();
}
}
}
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/rotationresolver/RotationResolverManagerPerUserService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
index 8a1c778..3dbc32a 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
@@ -159,7 +159,7 @@
final Intent intent = new Intent(
RotationResolverService.SERVICE_INTERFACE).setPackage(resolvedPackage);
- final ResolveInfo resolveInfo = context.getPackageManager().resolveActivityAsUser(intent,
+ final ResolveInfo resolveInfo = context.getPackageManager().resolveServiceAsUser(intent,
flags, context.getUserId());
if (resolveInfo == null || resolveInfo.serviceInfo == null) {
Slog.wtf(TAG, String.format("Service %s not found in package %s",
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java
similarity index 91%
rename from services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
rename to services/core/java/com/android/server/timedetector/EnvironmentImpl.java
index 4f3f9dc..5cd1718 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
+++ b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java
@@ -25,7 +25,6 @@
import android.content.ContentResolver;
import android.content.Context;
import android.os.Build;
-import android.os.Environment;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -39,11 +38,11 @@
import java.util.Objects;
/**
- * The real implementation of {@link TimeDetectorStrategyImpl.Callback} used on device.
+ * The real implementation of {@link TimeDetectorStrategyImpl.Environment} used on device.
*/
-public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrategyImpl.Callback {
+public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment {
- private final static String TAG = "timedetector.TimeDetectorStrategyCallbackImpl";
+ private static final String TAG = TimeDetectorService.TAG;
private static final int SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT = 2 * 1000;
@@ -52,7 +51,7 @@
* incorrect for sure.
*/
private static final Instant TIME_LOWER_BOUND = Instant.ofEpochMilli(
- Long.max(Environment.getRootDirectory().lastModified(), Build.TIME));
+ Long.max(android.os.Environment.getRootDirectory().lastModified(), Build.TIME));
/**
* By default telephony and network only suggestions are accepted and telephony takes
@@ -74,7 +73,7 @@
@NonNull private final AlarmManager mAlarmManager;
@NonNull private final int[] mOriginPriorities;
- public TimeDetectorStrategyCallbackImpl(@NonNull Context context) {
+ public EnvironmentImpl(@NonNull Context context) {
mContext = Objects.requireNonNull(context);
mContentResolver = Objects.requireNonNull(context.getContentResolver());
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 71b1a49..bbbd19f 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -50,7 +50,7 @@
* implementation to deal with the logic around time detection.
*/
public final class TimeDetectorService extends ITimeDetectorService.Stub {
- private static final String TAG = "TimeDetectorService";
+ static final String TAG = "time_detector";
public static class Lifecycle extends SystemService {
@@ -73,8 +73,8 @@
@NonNull private final TimeDetectorStrategy mTimeDetectorStrategy;
private static TimeDetectorService create(@NonNull Context context) {
- TimeDetectorStrategyImpl.Callback callback = new TimeDetectorStrategyCallbackImpl(context);
- TimeDetectorStrategy timeDetectorStrategy = new TimeDetectorStrategyImpl(callback);
+ TimeDetectorStrategyImpl.Environment environment = new EnvironmentImpl(context);
+ TimeDetectorStrategy timeDetectorStrategy = new TimeDetectorStrategyImpl(environment);
Handler handler = FgThread.getHandler();
TimeDetectorService timeDetectorService =
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index 2fbeb75..7cd4184 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -40,6 +40,7 @@
import java.time.Instant;
import java.util.Arrays;
+import java.util.Objects;
/**
* An implementation of {@link TimeDetectorStrategy} that passes telephony and manual suggestions to
@@ -51,7 +52,7 @@
public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
private static final boolean DBG = false;
- private static final String LOG_TAG = "SimpleTimeDetectorStrategy";
+ private static final String LOG_TAG = TimeDetectorService.TAG;
/** A score value used to indicate "no score", either due to validation failure or age. */
private static final int TELEPHONY_INVALID_SCORE = -1;
@@ -88,7 +89,7 @@
private final LocalLog mTimeChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
@NonNull
- private final Callback mCallback;
+ private final Environment mEnvironment;
// Used to store the last time the system clock state was set automatically. It is used to
// detect (and log) issues with the realtime clock or whether the clock is being set without
@@ -127,7 +128,7 @@
* moved to {@link TimeDetectorStrategy}. There are similar issues with
* {@link #systemClockMillis()} while any process can modify the system clock.
*/
- public interface Callback {
+ public interface Environment {
/**
* The absolute threshold below which the system clock need not be updated. i.e. if setting
@@ -170,8 +171,8 @@
void releaseWakeLock();
}
- TimeDetectorStrategyImpl(@NonNull Callback callback) {
- mCallback = callback;
+ TimeDetectorStrategyImpl(@NonNull Environment environment) {
+ mEnvironment = Objects.requireNonNull(environment);
}
@Override
@@ -267,7 +268,7 @@
@Override
public synchronized void handleAutoTimeConfigChanged() {
- boolean enabled = mCallback.isAutoTimeDetectionEnabled();
+ boolean enabled = mEnvironment.isAutoTimeDetectionEnabled();
// When automatic time detection is enabled we update the system clock instantly if we can.
// Conversely, when automatic time detection is disabled we leave the clock as it is.
if (enabled) {
@@ -286,20 +287,20 @@
ipw.increaseIndent(); // level 1
ipw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet);
- ipw.println("mCallback.isAutoTimeDetectionEnabled()="
- + mCallback.isAutoTimeDetectionEnabled());
- ipw.println("mCallback.elapsedRealtimeMillis()=" + mCallback.elapsedRealtimeMillis());
- ipw.println("mCallback.systemClockMillis()=" + mCallback.systemClockMillis());
- ipw.println("mCallback.systemClockUpdateThresholdMillis()="
- + mCallback.systemClockUpdateThresholdMillis());
- ipw.printf("mCallback.autoTimeLowerBound()=%s(%s)\n",
- mCallback.autoTimeLowerBound(),
- mCallback.autoTimeLowerBound().toEpochMilli());
+ ipw.println("mEnvironment.isAutoTimeDetectionEnabled()="
+ + mEnvironment.isAutoTimeDetectionEnabled());
+ ipw.println("mEnvironment.elapsedRealtimeMillis()=" + mEnvironment.elapsedRealtimeMillis());
+ ipw.println("mEnvironment.systemClockMillis()=" + mEnvironment.systemClockMillis());
+ ipw.println("mEnvironment.systemClockUpdateThresholdMillis()="
+ + mEnvironment.systemClockUpdateThresholdMillis());
+ ipw.printf("mEnvironment.autoTimeLowerBound()=%s(%s)\n",
+ mEnvironment.autoTimeLowerBound(),
+ mEnvironment.autoTimeLowerBound().toEpochMilli());
String priorities =
- Arrays.stream(mCallback.autoOriginPriorities())
+ Arrays.stream(mEnvironment.autoOriginPriorities())
.mapToObj(TimeDetectorStrategy::originToString)
.collect(joining(",", "[", "]"));
- ipw.println("mCallback.autoOriginPriorities()=" + priorities);
+ ipw.println("mEnvironment.autoOriginPriorities()=" + priorities);
ipw.println("Time change log:");
ipw.increaseIndent(); // level 2
@@ -372,7 +373,7 @@
}
// We can validate the suggestion against the reference time clock.
- long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
+ long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis();
if (elapsedRealtimeMillis < newUtcTime.getReferenceTimeMillis()) {
// elapsedRealtime clock went backwards?
Slog.w(LOG_TAG, "New reference time is in the future? Ignoring."
@@ -391,7 +392,7 @@
private boolean validateSuggestionAgainstLowerBound(
@NonNull TimestampedValue<Long> newUtcTime, @NonNull Object suggestion) {
- Instant lowerBound = mCallback.autoTimeLowerBound();
+ Instant lowerBound = mEnvironment.autoTimeLowerBound();
// Suggestion is definitely wrong if it comes before lower time bound.
if (lowerBound.isAfter(Instant.ofEpochMilli(newUtcTime.getValue()))) {
@@ -405,13 +406,13 @@
@GuardedBy("this")
private void doAutoTimeDetection(@NonNull String detectionReason) {
- if (!mCallback.isAutoTimeDetectionEnabled()) {
+ if (!mEnvironment.isAutoTimeDetectionEnabled()) {
// Avoid doing unnecessary work with this (race-prone) check.
return;
}
// Try the different origins one at a time.
- int[] originPriorities = mCallback.autoOriginPriorities();
+ int[] originPriorities = mEnvironment.autoOriginPriorities();
for (int origin : originPriorities) {
TimestampedValue<Long> newUtcTime = null;
String cause = null;
@@ -470,7 +471,7 @@
@GuardedBy("this")
@Nullable
private TelephonyTimeSuggestion findBestTelephonySuggestion() {
- long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
+ long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis();
// Telephony time suggestions are assumed to be derived from NITZ or NITZ-like signals.
// These have a number of limitations:
@@ -579,7 +580,7 @@
}
TimestampedValue<Long> utcTime = networkSuggestion.getUtcTime();
- long elapsedRealTimeMillis = mCallback.elapsedRealtimeMillis();
+ long elapsedRealTimeMillis = mEnvironment.elapsedRealtimeMillis();
if (!validateSuggestionUtcTime(elapsedRealTimeMillis, utcTime)) {
// The latest suggestion is not valid, usually due to its age.
return null;
@@ -599,7 +600,7 @@
}
TimestampedValue<Long> utcTime = gnssTimeSuggestion.getUtcTime();
- long elapsedRealTimeMillis = mCallback.elapsedRealtimeMillis();
+ long elapsedRealTimeMillis = mEnvironment.elapsedRealtimeMillis();
if (!validateSuggestionUtcTime(elapsedRealTimeMillis, utcTime)) {
// The latest suggestion is not valid, usually due to its age.
return null;
@@ -619,7 +620,7 @@
}
TimestampedValue<Long> utcTime = externalTimeSuggestion.getUtcTime();
- long elapsedRealTimeMillis = mCallback.elapsedRealtimeMillis();
+ long elapsedRealTimeMillis = mEnvironment.elapsedRealtimeMillis();
if (!validateSuggestionUtcTime(elapsedRealTimeMillis, utcTime)) {
// The latest suggestion is not valid, usually due to its age.
return null;
@@ -634,7 +635,7 @@
boolean isOriginAutomatic = isOriginAutomatic(origin);
if (isOriginAutomatic) {
- if (!mCallback.isAutoTimeDetectionEnabled()) {
+ if (!mEnvironment.isAutoTimeDetectionEnabled()) {
if (DBG) {
Slog.d(LOG_TAG, "Auto time detection is not enabled."
+ " origin=" + originToString(origin)
@@ -644,7 +645,7 @@
return false;
}
} else {
- if (mCallback.isAutoTimeDetectionEnabled()) {
+ if (mEnvironment.isAutoTimeDetectionEnabled()) {
if (DBG) {
Slog.d(LOG_TAG, "Auto time detection is enabled."
+ " origin=" + originToString(origin)
@@ -655,11 +656,11 @@
}
}
- mCallback.acquireWakeLock();
+ mEnvironment.acquireWakeLock();
try {
return setSystemClockUnderWakeLock(origin, time, cause);
} finally {
- mCallback.releaseWakeLock();
+ mEnvironment.releaseWakeLock();
}
}
@@ -671,9 +672,9 @@
private boolean setSystemClockUnderWakeLock(
@Origin int origin, @NonNull TimestampedValue<Long> newTime, @NonNull String cause) {
- long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
+ long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis();
boolean isOriginAutomatic = isOriginAutomatic(origin);
- long actualSystemClockMillis = mCallback.systemClockMillis();
+ long actualSystemClockMillis = mEnvironment.systemClockMillis();
if (isOriginAutomatic) {
// CLOCK_PARANOIA : Check to see if this class owns the clock or if something else
// may be setting the clock.
@@ -701,7 +702,7 @@
// Check if the new signal would make sufficient difference to the system clock. If it's
// below the threshold then ignore it.
long absTimeDifference = Math.abs(newSystemClockMillis - actualSystemClockMillis);
- long systemClockUpdateThreshold = mCallback.systemClockUpdateThresholdMillis();
+ long systemClockUpdateThreshold = mEnvironment.systemClockUpdateThresholdMillis();
if (absTimeDifference < systemClockUpdateThreshold) {
if (DBG) {
Slog.d(LOG_TAG, "Not setting system clock. New time and"
@@ -715,7 +716,7 @@
return true;
}
- mCallback.setSystemClock(newSystemClockMillis);
+ mEnvironment.setSystemClock(newSystemClockMillis);
String logMsg = "Set system clock using time=" + newTime
+ " cause=" + cause
+ " elapsedRealtimeMillis=" + elapsedRealtimeMillis
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
similarity index 94%
rename from services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
rename to services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
index 1357608..3340792 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
@@ -44,30 +44,30 @@
import java.util.Objects;
/**
- * The real implementation of {@link TimeZoneDetectorStrategyImpl.Callback}.
+ * The real implementation of {@link TimeZoneDetectorStrategyImpl.Environment}.
*/
-public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrategyImpl.Callback {
+public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Environment {
- private static final String LOG_TAG = "TimeZoneDetectorCallbackImpl";
+ private static final String LOG_TAG = TimeZoneDetectorService.TAG;
private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
@NonNull private final Context mContext;
@NonNull private final Handler mHandler;
@NonNull private final ContentResolver mCr;
@NonNull private final UserManager mUserManager;
- @NonNull private final boolean mGeoDetectionFeatureEnabled;
+ @NonNull private final boolean mGeoDetectionSupported;
@NonNull private final LocationManager mLocationManager;
// @NonNull after setConfigChangeListener() is called.
private ConfigurationChangeListener mConfigChangeListener;
- TimeZoneDetectorCallbackImpl(@NonNull Context context, @NonNull Handler handler,
- boolean geoDetectionFeatureEnabled) {
+ EnvironmentImpl(@NonNull Context context, @NonNull Handler handler,
+ boolean geoDetectionSupported) {
mContext = Objects.requireNonNull(context);
mHandler = Objects.requireNonNull(handler);
mCr = context.getContentResolver();
mUserManager = context.getSystemService(UserManager.class);
mLocationManager = context.getSystemService(LocationManager.class);
- mGeoDetectionFeatureEnabled = geoDetectionFeatureEnabled;
+ mGeoDetectionSupported = geoDetectionSupported;
// Wire up the change listener. All invocations are performed on the mHandler thread.
@@ -191,7 +191,7 @@
}
private boolean isGeoDetectionSupported() {
- return mGeoDetectionFeatureEnabled;
+ return mGeoDetectionSupported;
}
private boolean isAutoDetectionEnabled() {
diff --git a/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java b/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
index b63df05..4eb1b99 100644
--- a/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
+++ b/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
@@ -19,8 +19,11 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.SystemClock;
+import android.os.TimestampedValue;
import android.util.IndentingPrintWriter;
+import java.time.Duration;
import java.util.ArrayDeque;
/**
@@ -49,18 +52,15 @@
*/
public final class ReferenceWithHistory<V> {
- private static final Object NULL_MARKER = "{null marker}";
-
/** The maximum number of references to store. */
private final int mMaxHistorySize;
- /**
- * The history storage. Note that ArrayDeque doesn't support {@code null} so this stores Object
- * and not V. Use {@link #packNullIfRequired(Object)} and {@link #unpackNullIfRequired(Object)}
- * to convert to / from the storage object.
- */
+ /** The number of times {@link #set(Object)} has been called. */
+ private int mSetCount;
+
+ /** The history storage. */
@Nullable
- private ArrayDeque<Object> mValues;
+ private ArrayDeque<TimestampedValue<V>> mValues;
/**
* Creates an instance that records, at most, the specified number of values.
@@ -78,8 +78,8 @@
if (mValues == null || mValues.isEmpty()) {
return null;
}
- Object value = mValues.getFirst();
- return unpackNullIfRequired(value);
+ TimestampedValue<V> valueHolder = mValues.getFirst();
+ return valueHolder.getValue();
}
/**
@@ -98,8 +98,10 @@
V previous = get();
- Object nullSafeValue = packNullIfRequired(newValue);
- mValues.addFirst(nullSafeValue);
+ TimestampedValue<V> valueHolder =
+ new TimestampedValue<>(SystemClock.elapsedRealtime(), newValue);
+ mValues.addFirst(valueHolder);
+ mSetCount++;
return previous;
}
@@ -110,10 +112,13 @@
if (mValues == null) {
ipw.println("{Empty}");
} else {
- int i = 0;
- for (Object value : mValues) {
- ipw.println(i + ": " + unpackNullIfRequired(value));
- i++;
+ int i = mSetCount;
+ for (TimestampedValue<V> valueHolder : mValues) {
+ ipw.print(--i);
+ ipw.print("@");
+ ipw.print(Duration.ofMillis(valueHolder.getReferenceTimeMillis()).toString());
+ ipw.print(": ");
+ ipw.println(valueHolder.getValue());
}
}
ipw.flush();
@@ -130,23 +135,4 @@
public String toString() {
return String.valueOf(get());
}
-
- /**
- * Turns a non-nullable Object into a nullable value. See also
- * {@link #packNullIfRequired(Object)}.
- */
- @SuppressWarnings("unchecked")
- @Nullable
- private V unpackNullIfRequired(@NonNull Object value) {
- return value == NULL_MARKER ? null : (V) value;
- }
-
- /**
- * Turns a nullable value into a non-nullable Object. See also
- * {@link #unpackNullIfRequired(Object)}.
- */
- @NonNull
- private Object packNullIfRequired(@Nullable V value) {
- return value == null ? NULL_MARKER : value;
- }
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index 2ead3be..bd71ddf 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -59,7 +59,7 @@
public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
implements IBinder.DeathRecipient {
- private static final String TAG = "TimeZoneDetectorService";
+ static final String TAG = "time_zone_detector";
/**
* A "feature switch" for location-based time zone detection. If this is {@code false}. It is
@@ -67,19 +67,19 @@
* is important.
*/
@Nullable
- private static Boolean sGeoLocationTimeZoneDetectionEnabled;
+ private static Boolean sGeoLocationTimeZoneDetectionSupported;
/** Returns {@code true} if the location-based time zone detection feature is enabled. */
- public static boolean isGeoLocationTimeZoneDetectionEnabled(Context context) {
- if (sGeoLocationTimeZoneDetectionEnabled == null) {
+ public static boolean isGeoLocationTimeZoneDetectionSupported(Context context) {
+ if (sGeoLocationTimeZoneDetectionSupported == null) {
// The config value is expected to be the main switch. Platform developers can also
// enable the feature using a persistent system property.
- sGeoLocationTimeZoneDetectionEnabled = context.getResources().getBoolean(
+ sGeoLocationTimeZoneDetectionSupported = context.getResources().getBoolean(
com.android.internal.R.bool.config_enableGeolocationTimeZoneDetection)
|| SystemProperties.getBoolean(
"persist.sys.location_time_zone_detection_feature_enabled", false);
}
- return sGeoLocationTimeZoneDetectionEnabled;
+ return sGeoLocationTimeZoneDetectionSupported;
}
/**
@@ -98,11 +98,11 @@
Context context = getContext();
Handler handler = FgThread.getHandler();
- boolean geolocationTimeZoneDetectionEnabled =
- isGeoLocationTimeZoneDetectionEnabled(context);
+ boolean geolocationTimeZoneDetectionSupported =
+ isGeoLocationTimeZoneDetectionSupported(context);
TimeZoneDetectorStrategy timeZoneDetectorStrategy =
TimeZoneDetectorStrategyImpl.create(
- context, handler, geolocationTimeZoneDetectionEnabled);
+ context, handler, geolocationTimeZoneDetectionSupported);
// Create and publish the local service for use by internal callers.
TimeZoneDetectorInternal internal =
@@ -330,7 +330,7 @@
boolean isGeoTimeZoneDetectionSupported() {
enforceManageTimeZoneDetectorPermission();
- return isGeoLocationTimeZoneDetectionEnabled(mContext);
+ return isGeoLocationTimeZoneDetectionSupported(mContext);
}
@Override
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index 781668b..cac1a6d 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -59,7 +59,7 @@
* conditions.
*/
@VisibleForTesting
- public interface Callback {
+ public interface Environment {
/**
* Sets a {@link ConfigurationChangeListener} that will be invoked when there are any
@@ -97,7 +97,7 @@
void storeConfiguration(@UserIdInt int userId, TimeZoneConfiguration newConfiguration);
}
- private static final String LOG_TAG = "TimeZoneDetectorStrategy";
+ private static final String LOG_TAG = TimeZoneDetectorService.TAG;
private static final boolean DBG = false;
/**
@@ -164,7 +164,7 @@
private static final int KEEP_SUGGESTION_HISTORY_SIZE = 10;
@NonNull
- private final Callback mCallback;
+ private final Environment mEnvironment;
@GuardedBy("this")
@NonNull
@@ -203,17 +203,17 @@
*/
public static TimeZoneDetectorStrategyImpl create(
@NonNull Context context, @NonNull Handler handler,
- boolean geolocationTimeZoneDetectionEnabled) {
+ boolean geolocationTimeZoneDetectionSupported) {
- TimeZoneDetectorCallbackImpl callback = new TimeZoneDetectorCallbackImpl(
- context, handler, geolocationTimeZoneDetectionEnabled);
- return new TimeZoneDetectorStrategyImpl(callback);
+ EnvironmentImpl environment = new EnvironmentImpl(
+ context, handler, geolocationTimeZoneDetectionSupported);
+ return new TimeZoneDetectorStrategyImpl(environment);
}
@VisibleForTesting
- public TimeZoneDetectorStrategyImpl(@NonNull Callback callback) {
- mCallback = Objects.requireNonNull(callback);
- mCallback.setConfigChangeListener(this::handleConfigChanged);
+ public TimeZoneDetectorStrategyImpl(@NonNull Environment environment) {
+ mEnvironment = Objects.requireNonNull(environment);
+ mEnvironment.setConfigChangeListener(this::handleConfigChanged);
}
/**
@@ -230,13 +230,13 @@
@Override
@NonNull
public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) {
- return mCallback.getConfigurationInternal(userId);
+ return mEnvironment.getConfigurationInternal(userId);
}
@Override
@NonNull
public synchronized ConfigurationInternal getCurrentUserConfigurationInternal() {
- int currentUserId = mCallback.getCurrentUserId();
+ int currentUserId = mEnvironment.getCurrentUserId();
return getConfigurationInternal(currentUserId);
}
@@ -257,9 +257,9 @@
return false;
}
- // Store the configuration / notify as needed. This will cause the mCallback to invoke
+ // Store the configuration / notify as needed. This will cause the mEnvironment to invoke
// handleConfigChanged() asynchronously.
- mCallback.storeConfiguration(userId, newConfiguration);
+ mEnvironment.storeConfiguration(userId, newConfiguration);
String logMsg = "Configuration changed:"
+ " oldConfiguration=" + oldConfiguration
@@ -275,8 +275,9 @@
public synchronized void suggestGeolocationTimeZone(
@NonNull GeolocationTimeZoneSuggestion suggestion) {
- int currentUserId = mCallback.getCurrentUserId();
- ConfigurationInternal currentUserConfig = mCallback.getConfigurationInternal(currentUserId);
+ int currentUserId = mEnvironment.getCurrentUserId();
+ ConfigurationInternal currentUserConfig =
+ mEnvironment.getConfigurationInternal(currentUserId);
if (DBG) {
Slog.d(LOG_TAG, "Geolocation suggestion received."
+ " currentUserConfig=" + currentUserConfig
@@ -299,7 +300,7 @@
public synchronized boolean suggestManualTimeZone(
@UserIdInt int userId, @NonNull ManualTimeZoneSuggestion suggestion) {
- int currentUserId = mCallback.getCurrentUserId();
+ int currentUserId = mEnvironment.getCurrentUserId();
if (userId != currentUserId) {
Slog.w(LOG_TAG, "Manual suggestion received but user != current user, userId=" + userId
+ " suggestion=" + suggestion);
@@ -332,8 +333,9 @@
public synchronized void suggestTelephonyTimeZone(
@NonNull TelephonyTimeZoneSuggestion suggestion) {
- int currentUserId = mCallback.getCurrentUserId();
- ConfigurationInternal currentUserConfig = mCallback.getConfigurationInternal(currentUserId);
+ int currentUserId = mEnvironment.getCurrentUserId();
+ ConfigurationInternal currentUserConfig =
+ mEnvironment.getConfigurationInternal(currentUserId);
if (DBG) {
Slog.d(LOG_TAG, "Telephony suggestion received. currentUserConfig=" + currentUserConfig
+ " newSuggestion=" + suggestion);
@@ -423,7 +425,7 @@
String zoneId;
// Introduce bias towards the device's current zone when there are multiple zone suggested.
- String deviceTimeZone = mCallback.getDeviceTimeZone();
+ String deviceTimeZone = mEnvironment.getDeviceTimeZone();
if (zoneIds.contains(deviceTimeZone)) {
if (DBG) {
Slog.d(LOG_TAG,
@@ -486,7 +488,7 @@
@GuardedBy("this")
private void setDeviceTimeZoneIfRequired(@NonNull String newZoneId, @NonNull String cause) {
- String currentZoneId = mCallback.getDeviceTimeZone();
+ String currentZoneId = mEnvironment.getDeviceTimeZone();
// Avoid unnecessary changes / intents.
if (newZoneId.equals(currentZoneId)) {
@@ -501,7 +503,7 @@
return;
}
- mCallback.setDeviceTimeZone(newZoneId);
+ mEnvironment.setDeviceTimeZone(newZoneId);
String msg = "Set device time zone."
+ ", currentZoneId=" + currentZoneId
+ ", newZoneId=" + newZoneId
@@ -572,8 +574,9 @@
// This method is called whenever the user changes or the config for any user changes. We
// don't know what happened, so we capture the current user's config, check to see if we
// need to clear state associated with a previous user, and rerun detection.
- int currentUserId = mCallback.getCurrentUserId();
- ConfigurationInternal currentUserConfig = mCallback.getConfigurationInternal(currentUserId);
+ int currentUserId = mEnvironment.getCurrentUserId();
+ ConfigurationInternal currentUserConfig =
+ mEnvironment.getConfigurationInternal(currentUserId);
GeolocationTimeZoneSuggestion latestGeoLocationSuggestion =
mLatestGeoLocationSuggestion.get();
@@ -603,14 +606,14 @@
ipw.println("TimeZoneDetectorStrategy:");
ipw.increaseIndent(); // level 1
- int currentUserId = mCallback.getCurrentUserId();
- ipw.println("mCallback.getCurrentUserId()=" + currentUserId);
- ConfigurationInternal configuration = mCallback.getConfigurationInternal(currentUserId);
- ipw.println("mCallback.getConfiguration(currentUserId)=" + configuration);
+ int currentUserId = mEnvironment.getCurrentUserId();
+ ipw.println("mEnvironment.getCurrentUserId()=" + currentUserId);
+ ConfigurationInternal configuration = mEnvironment.getConfigurationInternal(currentUserId);
+ ipw.println("mEnvironment.getConfiguration(currentUserId)=" + configuration);
ipw.println("[Capabilities=" + configuration.createCapabilitiesAndConfig() + "]");
- ipw.println("mCallback.isDeviceTimeZoneInitialized()="
- + mCallback.isDeviceTimeZoneInitialized());
- ipw.println("mCallback.getDeviceTimeZone()=" + mCallback.getDeviceTimeZone());
+ ipw.println("mEnvironment.isDeviceTimeZoneInitialized()="
+ + mEnvironment.isDeviceTimeZoneInitialized());
+ ipw.println("mEnvironment.getDeviceTimeZone()=" + mEnvironment.getDeviceTimeZone());
ipw.println("Time zone change log:");
ipw.increaseIndent(); // level 2
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
index 5bee7ee..220810f 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
@@ -74,11 +74,17 @@
* mode" where the real binder clients are replaced by {@link
* SimulatedLocationTimeZoneProviderProxy}. This means that the real client providers are never
* bound (ensuring no real location events will be received) and simulated events / behaviors
- * can be injected via the command line. To enter simulation mode for a provider, use
- * "{@code adb shell setprop persist.sys.location_tz_simulation_mode.<provider name> 1}" and reboot.
- * e.g. "{@code adb shell setprop persist.sys.location_tz_simulation_mode.primary 1}}"
- * Then use "{@code adb shell cmd location_time_zone_manager help}" for injection. Set the system
- * properties to "0" and reboot to return to exit simulation mode.
+ * can be injected via the command line.
+ *
+ * <p>To enter simulation mode for a provider, use {@code adb shell cmd location_time_zone_manager
+ * set_provider_mode_override <provider name> simulated} and restart the service with {@code
+ * adb shell cmd location_time_zone_manager stop} and {@code adb shell cmd
+ * location_time_zone_manager start}.
+ *
+ * <p>e.g. {@code adb shell cmd location_time_zone_manager set_provider_mode_override primary
+ * simulated}.
+ *
+ * <p>See {@code adb shell cmd location_time_zone_manager help}" for more options.
*/
public class LocationTimeZoneManagerService extends Binder {
@@ -96,7 +102,7 @@
@Override
public void onStart() {
Context context = getContext();
- if (TimeZoneDetectorService.isGeoLocationTimeZoneDetectionEnabled(context)) {
+ if (TimeZoneDetectorService.isGeoLocationTimeZoneDetectionSupported(context)) {
mService = new LocationTimeZoneManagerService(context);
// The service currently exposes no LocalService or Binder API, but it extends
@@ -110,7 +116,7 @@
@Override
public void onBootPhase(int phase) {
Context context = getContext();
- if (TimeZoneDetectorService.isGeoLocationTimeZoneDetectionEnabled(context)) {
+ if (TimeZoneDetectorService.isGeoLocationTimeZoneDetectionSupported(context)) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
// The location service must be functioning after this boot phase.
mService.onSystemReady();
diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
index 6427ae2..fd12c2d2 100644
--- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
+++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
@@ -18,16 +18,28 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.NetworkCapabilities.NetCapability;
+import android.net.NetworkRequest;
+import android.net.TelephonyNetworkSpecifier;
import android.os.Handler;
import android.os.ParcelUuid;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* Tracks a set of Networks underpinning a VcnGatewayConnection.
@@ -38,53 +50,385 @@
*
* @hide
*/
-public class UnderlyingNetworkTracker extends Handler {
+public class UnderlyingNetworkTracker {
@NonNull private static final String TAG = UnderlyingNetworkTracker.class.getSimpleName();
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final UnderlyingNetworkTrackerCallback mCb;
@NonNull private final Dependencies mDeps;
+ @NonNull private final Handler mHandler;
+ @NonNull private final ConnectivityManager mConnectivityManager;
+ @NonNull private final SubscriptionManager mSubscriptionManager;
+
+ @NonNull private final SparseArray<NetworkCallback> mCellBringupCallbacks = new SparseArray<>();
+ @NonNull private final NetworkCallback mWifiBringupCallback = new NetworkBringupCallback();
+ @NonNull private final NetworkCallback mRouteSelectionCallback = new RouteSelectionCallback();
+
+ @NonNull private final Set<Integer> mSubIds = new ArraySet<>();
+
+ @NonNull private final Set<Integer> mRequiredUnderlyingNetworkCapabilities;
+
+ @Nullable private UnderlyingNetworkRecord mCurrentRecord;
+ @Nullable private UnderlyingNetworkRecord.Builder mRecordInProgress;
public UnderlyingNetworkTracker(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
+ @NonNull Set<Integer> requiredUnderlyingNetworkCapabilities,
@NonNull UnderlyingNetworkTrackerCallback cb) {
- this(vcnContext, subscriptionGroup, cb, new Dependencies());
+ this(
+ vcnContext,
+ subscriptionGroup,
+ requiredUnderlyingNetworkCapabilities,
+ cb,
+ new Dependencies());
}
private UnderlyingNetworkTracker(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
+ @NonNull Set<Integer> requiredUnderlyingNetworkCapabilities,
@NonNull UnderlyingNetworkTrackerCallback cb,
@NonNull Dependencies deps) {
- super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
- mVcnContext = vcnContext;
+ mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext");
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
+ mRequiredUnderlyingNetworkCapabilities =
+ Objects.requireNonNull(
+ requiredUnderlyingNetworkCapabilities,
+ "Missing requiredUnderlyingNetworkCapabilities");
mCb = Objects.requireNonNull(cb, "Missing cb");
mDeps = Objects.requireNonNull(deps, "Missing deps");
+
+ mHandler = new Handler(mVcnContext.getLooper());
+
+ mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class);
+ mSubscriptionManager = mVcnContext.getContext().getSystemService(SubscriptionManager.class);
+
+ registerNetworkRequests();
+ }
+
+ private void registerNetworkRequests() {
+ // register bringup requests for underlying Networks
+ mConnectivityManager.requestBackgroundNetwork(
+ getWifiNetworkRequest(), mHandler, mWifiBringupCallback);
+ updateSubIdsAndCellularRequests();
+
+ // register Network-selection request used to decide selected underlying Network
+ mConnectivityManager.requestBackgroundNetwork(
+ getNetworkRequestBase().build(), mHandler, mRouteSelectionCallback);
+ }
+
+ private NetworkRequest getWifiNetworkRequest() {
+ return getNetworkRequestBase().addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
+ }
+
+ private NetworkRequest getCellNetworkRequestForSubId(int subId) {
+ return getNetworkRequestBase()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId))
+ .build();
+ }
+
+ private NetworkRequest.Builder getNetworkRequestBase() {
+ NetworkRequest.Builder requestBase = new NetworkRequest.Builder();
+ for (@NetCapability int capability : mRequiredUnderlyingNetworkCapabilities) {
+ requestBase.addCapability(capability);
+ }
+
+ return requestBase
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
+ .addUnwantedCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+ }
+
+ /**
+ * Update the current subIds and Cellular bringup requests for this UnderlyingNetworkTracker.
+ */
+ private void updateSubIdsAndCellularRequests() {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ Set<Integer> prevSubIds = new ArraySet<>(mSubIds);
+ mSubIds.clear();
+
+ // Ensure NetworkRequests filed for all current subIds in mSubscriptionGroup
+ // STOPSHIP: b/177364490 use TelephonySubscriptionSnapshot to avoid querying Telephony
+ List<SubscriptionInfo> subInfos =
+ mSubscriptionManager.getSubscriptionsInGroup(mSubscriptionGroup);
+
+ for (SubscriptionInfo subInfo : subInfos) {
+ final int subId = subInfo.getSubscriptionId();
+ mSubIds.add(subId);
+
+ if (!mCellBringupCallbacks.contains(subId)) {
+ final NetworkBringupCallback cb = new NetworkBringupCallback();
+ mCellBringupCallbacks.put(subId, cb);
+
+ mConnectivityManager.requestBackgroundNetwork(
+ getCellNetworkRequestForSubId(subId), mHandler, cb);
+ }
+ }
+
+ // unregister all NetworkCallbacks for outdated subIds
+ for (final int subId : prevSubIds) {
+ if (!mSubIds.contains(subId)) {
+ final NetworkCallback cb = mCellBringupCallbacks.removeReturnOld(subId);
+ mConnectivityManager.unregisterNetworkCallback(cb);
+ }
+ }
}
/** Tears down this Tracker, and releases all underlying network requests. */
- public void teardown() {}
+ public void teardown() {
+ mVcnContext.ensureRunningOnLooperThread();
- /** An record of a single underlying network, caching relevant fields. */
+ mConnectivityManager.unregisterNetworkCallback(mWifiBringupCallback);
+ mConnectivityManager.unregisterNetworkCallback(mRouteSelectionCallback);
+
+ for (final int subId : mSubIds) {
+ final NetworkCallback cb = mCellBringupCallbacks.removeReturnOld(subId);
+ mConnectivityManager.unregisterNetworkCallback(cb);
+ }
+ mSubIds.clear();
+ }
+
+ /** Returns whether the currently selected Network matches the given network. */
+ private static boolean isSameNetwork(
+ @Nullable UnderlyingNetworkRecord.Builder recordInProgress, @NonNull Network network) {
+ return recordInProgress != null && recordInProgress.getNetwork().equals(network);
+ }
+
+ /** Notify the Callback if a full UnderlyingNetworkRecord exists. */
+ private void maybeNotifyCallback() {
+ // Only forward this update if a complete record has been received
+ if (!mRecordInProgress.isValid()) {
+ return;
+ }
+
+ // Only forward this update if the updated record differs form the current record
+ UnderlyingNetworkRecord updatedRecord = mRecordInProgress.build();
+ if (!updatedRecord.equals(mCurrentRecord)) {
+ mCurrentRecord = updatedRecord;
+
+ mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord);
+ }
+ }
+
+ private void handleNetworkAvailable(@NonNull Network network) {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ mRecordInProgress = new UnderlyingNetworkRecord.Builder(network);
+ }
+
+ private void handleNetworkLost(@NonNull Network network) {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ if (!isSameNetwork(mRecordInProgress, network)) {
+ Slog.wtf(TAG, "Non-underlying Network lost");
+ return;
+ }
+
+ mRecordInProgress = null;
+ mCurrentRecord = null;
+ mCb.onSelectedUnderlyingNetworkChanged(null /* underlyingNetworkRecord */);
+ }
+
+ private void handleCapabilitiesChanged(
+ @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ if (!isSameNetwork(mRecordInProgress, network)) {
+ Slog.wtf(TAG, "Invalid update to NetworkCapabilities");
+ return;
+ }
+
+ mRecordInProgress.setNetworkCapabilities(networkCapabilities);
+
+ maybeNotifyCallback();
+ }
+
+ private void handleNetworkSuspended(@NonNull Network network, boolean isSuspended) {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ if (!isSameNetwork(mRecordInProgress, network)) {
+ Slog.wtf(TAG, "Invalid update to isSuspended");
+ return;
+ }
+
+ final NetworkCapabilities newCaps =
+ new NetworkCapabilities(mRecordInProgress.getNetworkCapabilities());
+ if (isSuspended) {
+ newCaps.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
+ } else {
+ newCaps.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
+ }
+
+ handleCapabilitiesChanged(network, newCaps);
+ }
+
+ private void handlePropertiesChanged(
+ @NonNull Network network, @NonNull LinkProperties linkProperties) {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ if (!isSameNetwork(mRecordInProgress, network)) {
+ Slog.wtf(TAG, "Invalid update to LinkProperties");
+ return;
+ }
+
+ mRecordInProgress.setLinkProperties(linkProperties);
+
+ maybeNotifyCallback();
+ }
+
+ private void handleNetworkBlocked(@NonNull Network network, boolean isBlocked) {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ if (!isSameNetwork(mRecordInProgress, network)) {
+ Slog.wtf(TAG, "Invalid update to isBlocked");
+ return;
+ }
+
+ mRecordInProgress.setIsBlocked(isBlocked);
+
+ maybeNotifyCallback();
+ }
+
+ /**
+ * NetworkBringupCallback is used to keep background, VCN-managed Networks from being reaped.
+ *
+ * <p>NetworkBringupCallback only exists to prevent matching (VCN-managed) Networks from being
+ * reaped, and no action is taken on any events firing.
+ */
+ @VisibleForTesting
+ class NetworkBringupCallback extends NetworkCallback {}
+
+ /**
+ * RouteSelectionCallback is used to select the "best" underlying Network.
+ *
+ * <p>The "best" network is determined by ConnectivityService, which is treated as a source of
+ * truth.
+ */
+ @VisibleForTesting
+ class RouteSelectionCallback extends NetworkCallback {
+ @Override
+ public void onAvailable(@NonNull Network network) {
+ handleNetworkAvailable(network);
+ }
+
+ @Override
+ public void onLost(@NonNull Network network) {
+ handleNetworkLost(network);
+ }
+
+ @Override
+ public void onCapabilitiesChanged(
+ @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
+ handleCapabilitiesChanged(network, networkCapabilities);
+ }
+
+ @Override
+ public void onNetworkSuspended(@NonNull Network network) {
+ handleNetworkSuspended(network, true /* isSuspended */);
+ }
+
+ @Override
+ public void onNetworkResumed(@NonNull Network network) {
+ handleNetworkSuspended(network, false /* isSuspended */);
+ }
+
+ @Override
+ public void onLinkPropertiesChanged(
+ @NonNull Network network, @NonNull LinkProperties linkProperties) {
+ handlePropertiesChanged(network, linkProperties);
+ }
+
+ @Override
+ public void onBlockedStatusChanged(@NonNull Network network, boolean isBlocked) {
+ handleNetworkBlocked(network, isBlocked);
+ }
+ }
+
+ /** A record of a single underlying network, caching relevant fields. */
public static class UnderlyingNetworkRecord {
@NonNull public final Network network;
@NonNull public final NetworkCapabilities networkCapabilities;
@NonNull public final LinkProperties linkProperties;
- public final boolean blocked;
+ public final boolean isBlocked;
@VisibleForTesting(visibility = Visibility.PRIVATE)
UnderlyingNetworkRecord(
@NonNull Network network,
@NonNull NetworkCapabilities networkCapabilities,
@NonNull LinkProperties linkProperties,
- boolean blocked) {
+ boolean isBlocked) {
this.network = network;
this.networkCapabilities = networkCapabilities;
this.linkProperties = linkProperties;
- this.blocked = blocked;
+ this.isBlocked = isBlocked;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof UnderlyingNetworkRecord)) return false;
+ final UnderlyingNetworkRecord that = (UnderlyingNetworkRecord) o;
+
+ return network.equals(that.network)
+ && networkCapabilities.equals(that.networkCapabilities)
+ && linkProperties.equals(that.linkProperties)
+ && isBlocked == that.isBlocked;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(network, networkCapabilities, linkProperties, isBlocked);
+ }
+
+ /** Builder to incrementally construct an UnderlyingNetworkRecord. */
+ private static class Builder {
+ @NonNull private final Network mNetwork;
+
+ @Nullable private NetworkCapabilities mNetworkCapabilities;
+ @Nullable private LinkProperties mLinkProperties;
+ boolean mIsBlocked;
+ boolean mWasIsBlockedSet;
+
+ private Builder(@NonNull Network network) {
+ mNetwork = network;
+ }
+
+ @NonNull
+ private Network getNetwork() {
+ return mNetwork;
+ }
+
+ private void setNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
+ mNetworkCapabilities = networkCapabilities;
+ }
+
+ @Nullable
+ private NetworkCapabilities getNetworkCapabilities() {
+ return mNetworkCapabilities;
+ }
+
+ private void setLinkProperties(@NonNull LinkProperties linkProperties) {
+ mLinkProperties = linkProperties;
+ }
+
+ private void setIsBlocked(boolean isBlocked) {
+ mIsBlocked = isBlocked;
+ mWasIsBlockedSet = true;
+ }
+
+ private boolean isValid() {
+ return mNetworkCapabilities != null && mLinkProperties != null && mWasIsBlockedSet;
+ }
+
+ private UnderlyingNetworkRecord build() {
+ return new UnderlyingNetworkRecord(
+ mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked);
+ }
}
}
@@ -95,9 +439,10 @@
*
* <p>This callback does NOT signal a mobility event.
*
- * @param underlying The details of the new underlying network
+ * @param underlyingNetworkRecord The details of the new underlying network
*/
- void onSelectedUnderlyingNetworkChanged(@Nullable UnderlyingNetworkRecord underlying);
+ void onSelectedUnderlyingNetworkChanged(
+ @Nullable UnderlyingNetworkRecord underlyingNetworkRecord);
}
private static class Dependencies {}
diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java
index dba59bd..7399e56 100644
--- a/services/core/java/com/android/server/vcn/VcnContext.java
+++ b/services/core/java/com/android/server/vcn/VcnContext.java
@@ -55,4 +55,15 @@
public VcnNetworkProvider getVcnNetworkProvider() {
return mVcnNetworkProvider;
}
+
+ /**
+ * Verifies that the caller is running on the VcnContext Thread.
+ *
+ * @throwsIllegalStateException if the caller is not running on the VcnContext Thread.
+ */
+ public void ensureRunningOnLooperThread() {
+ if (getLooper().getThread() != Thread.currentThread()) {
+ throw new IllegalStateException("Not running on VcnMgmtSvc thread");
+ }
+ }
}
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 3cfa00e..39c9606 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -65,6 +65,7 @@
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
@@ -476,7 +477,10 @@
mUnderlyingNetworkTracker =
mDeps.newUnderlyingNetworkTracker(
- mVcnContext, subscriptionGroup, mUnderlyingNetworkTrackerCallback);
+ mVcnContext,
+ subscriptionGroup,
+ mConnectionConfig.getAllUnderlyingCapabilities(),
+ mUnderlyingNetworkTrackerCallback);
mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class);
IpSecTunnelInterface iface;
@@ -1134,8 +1138,10 @@
public UnderlyingNetworkTracker newUnderlyingNetworkTracker(
VcnContext vcnContext,
ParcelUuid subscriptionGroup,
+ Set<Integer> requiredUnderlyingNetworkCapabilities,
UnderlyingNetworkTrackerCallback callback) {
- return new UnderlyingNetworkTracker(vcnContext, subscriptionGroup, callback);
+ return new UnderlyingNetworkTracker(
+ vcnContext, subscriptionGroup, requiredUnderlyingNetworkCapabilities, callback);
}
/** Builds a new IkeSession. */
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 536375f..8910bdf 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -34,10 +34,12 @@
import android.os.Vibrator;
import android.provider.Settings;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
+import com.android.server.VibratorServiceDumpProto;
import java.util.ArrayList;
import java.util.List;
@@ -340,6 +342,24 @@
+ '}';
}
+ /** Write current settings into given {@link ProtoOutputStream}. */
+ public void dumpProto(ProtoOutputStream proto) {
+ synchronized (mLock) {
+ proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY,
+ mHapticFeedbackIntensity);
+ proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_DEFAULT_INTENSITY,
+ mVibrator.getDefaultHapticFeedbackIntensity());
+ proto.write(VibratorServiceDumpProto.NOTIFICATION_INTENSITY,
+ mNotificationIntensity);
+ proto.write(VibratorServiceDumpProto.NOTIFICATION_DEFAULT_INTENSITY,
+ mVibrator.getDefaultNotificationVibrationIntensity());
+ proto.write(VibratorServiceDumpProto.RING_INTENSITY,
+ mRingIntensity);
+ proto.write(VibratorServiceDumpProto.RING_DEFAULT_INTENSITY,
+ mVibrator.getDefaultRingVibrationIntensity());
+ }
+ }
+
private void notifyListeners() {
List<OnVibratorSettingsChanged> currentListeners;
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index 6f391f3..4f2fc86 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -116,6 +116,9 @@
@Override
public void binderDied() {
+ if (DEBUG) {
+ Slog.d(TAG, "Binder died, cancelling vibration...");
+ }
cancel();
}
@@ -146,6 +149,9 @@
/** Notify current vibration that a step has completed on given vibrator. */
public void vibratorComplete(int vibratorId) {
synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration complete reported by vibrator " + vibratorId);
+ }
if (mCurrentVibrateStep != null) {
mCurrentVibrateStep.vibratorComplete(vibratorId);
}
@@ -467,7 +473,7 @@
noteVibratorOff();
}
if (DEBUG) {
- Slog.d(TAG, "SingleVibrateStep step done.");
+ Slog.d(TAG, "SingleVibrateStep done.");
}
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4e359f2..36c5037 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
@@ -81,6 +82,7 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.res.Configuration.EMPTY;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -201,9 +203,13 @@
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.ACTIVITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_SOLID_COLOR;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
@@ -249,6 +255,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
@@ -266,6 +273,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManager;
+import android.permission.PermissionManager;
import android.service.dreams.DreamActivity;
import android.service.dreams.DreamManagerInternal;
import android.service.voice.IVoiceInteractionSession;
@@ -321,6 +329,7 @@
import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.wm.Task.ActivityState;
import com.android.server.wm.WindowManagerService.H;
+import com.android.server.wm.WindowManagerService.LetterboxBackgroundType;
import com.android.server.wm.utils.InsetUtils;
import com.google.android.collect.Sets;
@@ -859,6 +868,9 @@
pw.print(Integer.toHexString(taskDescription.getStatusBarColor()));
pw.print(" navigationBarColor=");
pw.println(Integer.toHexString(taskDescription.getNavigationBarColor()));
+ pw.print(" backgroundColorFloating=");
+ pw.println(Integer.toHexString(
+ taskDescription.getBackgroundColorFloating()));
}
}
if (results != null) {
@@ -1361,7 +1373,8 @@
if (mLetterbox == null) {
mLetterbox = new Letterbox(() -> makeChildSurface(null),
mWmService.mTransactionFactory,
- mWmService::isLetterboxActivityCornersRounded);
+ mWmService::isLetterboxActivityCornersRounded,
+ this::getLetterboxBackgroundColor);
mLetterbox.attachInput(w);
}
getPosition(mTmpPoint);
@@ -1381,6 +1394,31 @@
}
}
+ private Color getLetterboxBackgroundColor() {
+ final WindowState w = findMainWindow();
+ if (w == null || w.isLetterboxedForDisplayCutout()) {
+ return Color.valueOf(Color.BLACK);
+ }
+ @LetterboxBackgroundType int letterboxBackgroundType =
+ mWmService.getLetterboxBackgroundType();
+ switch (letterboxBackgroundType) {
+ case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
+ if (taskDescription != null && taskDescription.getBackgroundColorFloating() != 0) {
+ return Color.valueOf(taskDescription.getBackgroundColorFloating());
+ }
+ return mWmService.getLetterboxBackgroundColor();
+ case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
+ if (taskDescription != null && taskDescription.getBackgroundColor() != 0) {
+ return Color.valueOf(taskDescription.getBackgroundColor());
+ }
+ // Falling through
+ case LETTERBOX_BACKGROUND_SOLID_COLOR:
+ return mWmService.getLetterboxBackgroundColor();
+ }
+ throw new AssertionError(
+ "Unexpected letterbox background type: " + letterboxBackgroundType);
+ }
+
/** @return {@code true} when main window is letterboxed and activity isn't transparent. */
private boolean isLetterboxed(WindowState mainWindow) {
return mainWindow.isLetterboxedAppWindow() && fillsParent();
@@ -6737,6 +6775,20 @@
// layout traversals.
mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
getResolvedOverrideConfiguration().seq = mConfigurationSeq;
+
+ // Sandbox max bounds by setting it to the app bounds, if activity is letterboxed or in
+ // size compat mode.
+ if (providesMaxBounds()) {
+ if (DEBUG_CONFIGURATION) {
+ ProtoLog.d(WM_DEBUG_CONFIGURATION, "Sandbox max bounds for uid %s to bounds %s "
+ + "due to letterboxing? %s mismatch with parent bounds? %s size compat "
+ + "mode %s", getUid(),
+ resolvedConfig.windowConfiguration.getBounds(), mLetterbox != null,
+ !matchParentBounds(), inSizeCompatMode());
+ }
+ resolvedConfig.windowConfiguration
+ .setMaxBounds(resolvedConfig.windowConfiguration.getBounds());
+ }
}
/**
@@ -6920,6 +6972,19 @@
return super.getBounds();
}
+ @Override
+ public boolean providesMaxBounds() {
+ // System and SystemUI should always be able to access the physical display bounds,
+ // so do not provide it with the overridden maximum bounds.
+ // TODO(b/179179513) check WindowState#mOwnerCanAddInternalSystemWindow instead
+ if (getUid() == SYSTEM_UID || PermissionManager.checkPermission(INTERNAL_SYSTEM_WINDOW,
+ getPid(), info.applicationInfo.uid) == PERMISSION_GRANTED) {
+ return false;
+ }
+ // Max bounds should be sandboxed when this is letterboxed or in size compat mode.
+ return mLetterbox != null || !matchParentBounds() || inSizeCompatMode();
+ }
+
@VisibleForTesting
@Override
Rect getAnimationBounds(int appRootTaskClipMode) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 64b1e042..a97eb7f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2457,6 +2457,13 @@
if (referrer != null) {
pae.extras.putParcelable(Intent.EXTRA_REFERRER, referrer);
}
+ if (!pae.activity.isAttached()) {
+ // Skip directly because the caller activity may have been destroyed. If a caller
+ // is waiting for the assist data, it will be notified by timeout
+ // (see PendingAssistExtras#run()) and then pendingAssistExtrasTimedOut will clean
+ // up the request.
+ return;
+ }
if (structure != null) {
// Pre-fill the task/activity component for all assist data receivers
structure.setTaskId(pae.activity.getTask().mTaskId);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index d5d06f9..3ab7952 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -204,6 +204,7 @@
import android.view.InsetsState.InternalInsetsType;
import android.view.MagnificationSpec;
import android.view.RemoteAnimationDefinition;
+import android.view.RoundedCorners;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
@@ -337,6 +338,10 @@
= new RotationCache<>(this::calculateDisplayCutoutForRotationUncached);
boolean mIgnoreDisplayCutout;
+ RoundedCorners mInitialRoundedCorners;
+ private final RotationCache<RoundedCorners, RoundedCorners> mRoundedCornerCache =
+ new RotationCache<>(this::calculateRoundedCornersForRotationUncached);
+
/**
* Overridden display size. Initialized with {@link #mInitialDisplayWidth}
* and {@link #mInitialDisplayHeight}, but can be set via shell command "adb shell wm size".
@@ -983,7 +988,8 @@
isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
mInsetsStateController = new InsetsStateController(this);
mDisplayFrames = new DisplayFrames(mDisplayId, mInsetsStateController.getRawInsetsState(),
- mDisplayInfo, calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
+ mDisplayInfo, calculateDisplayCutoutForRotation(mDisplayInfo.rotation),
+ calculateRoundedCornersForRotation(mDisplayInfo.rotation));
initializeDisplayBaseInfo();
mAppTransition = new AppTransition(mWmService.mContext, mWmService, this);
@@ -1696,8 +1702,9 @@
mTmpConfiguration.unset();
final DisplayInfo info = computeScreenConfiguration(mTmpConfiguration, rotation);
final WmDisplayCutout cutout = calculateDisplayCutoutForRotation(rotation);
+ final RoundedCorners roundedCorners = calculateRoundedCornersForRotation(rotation);
final DisplayFrames displayFrames = new DisplayFrames(mDisplayId, new InsetsState(), info,
- cutout);
+ cutout, roundedCorners);
token.applyFixedRotationTransform(info, displayFrames, mTmpConfiguration);
}
@@ -1958,6 +1965,23 @@
rotated ? mInitialDisplayWidth : mInitialDisplayHeight);
}
+ RoundedCorners calculateRoundedCornersForRotation(int rotation) {
+ return mRoundedCornerCache.getOrCompute(mInitialRoundedCorners, rotation);
+ }
+
+ private RoundedCorners calculateRoundedCornersForRotationUncached(
+ RoundedCorners roundedCorners, int rotation) {
+ if (roundedCorners == null || roundedCorners == RoundedCorners.NO_ROUNDED_CORNERS) {
+ return RoundedCorners.NO_ROUNDED_CORNERS;
+ }
+
+ if (rotation == ROTATION_0) {
+ return roundedCorners;
+ }
+
+ return roundedCorners.rotate(rotation, mInitialDisplayWidth, mInitialDisplayHeight);
+ }
+
/**
* Compute display info and configuration according to the given rotation without changing
* current display.
@@ -2485,7 +2509,8 @@
void onDisplayInfoChanged() {
final DisplayInfo info = mDisplayInfo;
- mDisplayFrames.onDisplayInfoUpdated(info, calculateDisplayCutoutForRotation(info.rotation));
+ mDisplayFrames.onDisplayInfoUpdated(info, calculateDisplayCutoutForRotation(info.rotation),
+ calculateRoundedCornersForRotation(info.rotation));
mInputMonitor.layoutInputConsumers(info.logicalWidth, info.logicalHeight);
mDisplayPolicy.onDisplayInfoChanged(info);
}
@@ -2518,6 +2543,7 @@
mInitialDisplayHeight = mDisplayInfo.logicalHeight;
mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi;
mInitialDisplayCutout = mDisplayInfo.displayCutout;
+ mInitialRoundedCorners = mDisplayInfo.roundedCorners;
}
/**
@@ -2535,11 +2561,13 @@
final DisplayCutout newCutout = mIgnoreDisplayCutout
? DisplayCutout.NO_CUTOUT : mDisplayInfo.displayCutout;
final String newUniqueId = mDisplayInfo.uniqueId;
+ final RoundedCorners newRoundedCorners = mDisplayInfo.roundedCorners;
final boolean displayMetricsChanged = mInitialDisplayWidth != newWidth
|| mInitialDisplayHeight != newHeight
|| mInitialDisplayDensity != mDisplayInfo.logicalDensityDpi
- || !Objects.equals(mInitialDisplayCutout, newCutout);
+ || !Objects.equals(mInitialDisplayCutout, newCutout)
+ || !Objects.equals(mInitialRoundedCorners, newRoundedCorners);
final boolean physicalDisplayChanged = !newUniqueId.equals(mCurrentUniqueDisplayId);
if (displayMetricsChanged || physicalDisplayChanged) {
@@ -2558,6 +2586,7 @@
mInitialDisplayHeight = newHeight;
mInitialDisplayDensity = newDensity;
mInitialDisplayCutout = newCutout;
+ mInitialRoundedCorners = newRoundedCorners;
mCurrentUniqueDisplayId = newUniqueId;
reconfigureDisplayLocked();
}
@@ -5500,12 +5529,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 +5541,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/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java
index 43c1435..e4230a2 100644
--- a/services/core/java/com/android/server/wm/DisplayFrames.java
+++ b/services/core/java/com/android/server/wm/DisplayFrames.java
@@ -21,12 +21,12 @@
import static android.view.InsetsState.ITYPE_RIGHT_DISPLAY_CUTOUT;
import static android.view.InsetsState.ITYPE_TOP_DISPLAY_CUTOUT;
-import android.annotation.NonNull;
import android.graphics.Rect;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.InsetsState;
+import android.view.RoundedCorners;
import com.android.server.wm.utils.WmDisplayCutout;
@@ -47,9 +47,6 @@
*/
public final Rect mUnrestricted = new Rect();
- /** The display cutout used for layout (after rotation) */
- @NonNull public WmDisplayCutout mDisplayCutout = WmDisplayCutout.NO_CUTOUT;
-
/**
* During layout, the frame that is display-cutout safe, i.e. that does not intersect with it.
*/
@@ -61,26 +58,37 @@
public int mRotation;
public DisplayFrames(int displayId, InsetsState insetsState, DisplayInfo info,
- WmDisplayCutout displayCutout) {
+ WmDisplayCutout displayCutout, RoundedCorners roundedCorners) {
mDisplayId = displayId;
mInsetsState = insetsState;
- onDisplayInfoUpdated(info, displayCutout);
+ onDisplayInfoUpdated(info, displayCutout, roundedCorners);
}
- public void onDisplayInfoUpdated(DisplayInfo info, WmDisplayCutout displayCutout) {
+ /**
+ * Update {@link DisplayFrames} when {@link DisplayInfo} is updated.
+ *
+ * @param info the updated {@link DisplayInfo}.
+ * @param displayCutout the updated {@link DisplayCutout}.
+ * @param roundedCorners the updated {@link RoundedCorners}.
+ */
+ public void onDisplayInfoUpdated(DisplayInfo info, WmDisplayCutout displayCutout,
+ RoundedCorners roundedCorners) {
mDisplayWidth = info.logicalWidth;
mDisplayHeight = info.logicalHeight;
mRotation = info.rotation;
- mDisplayCutout = displayCutout != null ? displayCutout : WmDisplayCutout.NO_CUTOUT;
+ final WmDisplayCutout wmDisplayCutout =
+ displayCutout != null ? displayCutout : WmDisplayCutout.NO_CUTOUT;
final InsetsState state = mInsetsState;
final Rect unrestricted = mUnrestricted;
final Rect safe = mDisplayCutoutSafe;
- final DisplayCutout cutout = mDisplayCutout.getDisplayCutout();
+ final DisplayCutout cutout = wmDisplayCutout.getDisplayCutout();
unrestricted.set(0, 0, mDisplayWidth, mDisplayHeight);
safe.set(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
state.setDisplayFrame(unrestricted);
state.setDisplayCutout(cutout);
+ state.setRoundedCorners(roundedCorners != null ? roundedCorners
+ : RoundedCorners.NO_ROUNDED_CORNERS);
if (!cutout.isEmpty()) {
if (cutout.getSafeInsetLeft() > 0) {
safe.left = unrestricted.left + cutout.getSafeInsetLeft();
@@ -118,12 +126,5 @@
public void dump(String prefix, PrintWriter pw) {
pw.println(prefix + "DisplayFrames w=" + mDisplayWidth + " h=" + mDisplayHeight
+ " r=" + mRotation);
- final String myPrefix = prefix + " ";
- dumpFrame(mUnrestricted, "mUnrestricted", myPrefix, pw);
- pw.println(myPrefix + "mDisplayCutout=" + mDisplayCutout);
- }
-
- private void dumpFrame(Rect frame, String name, String prefix, PrintWriter pw) {
- pw.print(prefix + name + "="); frame.printShortString(pw); pw.println();
}
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 3ba7b7d..580d328 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
@@ -218,6 +219,9 @@
state.removeSource(ITYPE_STATUS_BAR);
state.removeSource(ITYPE_NAVIGATION_BAR);
state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
+ if (windowingMode == WINDOWING_MODE_PINNED) {
+ state.removeSource(ITYPE_IME);
+ }
}
return state;
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 02a43b7..2274a4a 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -19,6 +19,7 @@
import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.view.SurfaceControl.HIDDEN;
+import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
@@ -45,6 +46,8 @@
private final Supplier<SurfaceControl.Builder> mSurfaceControlFactory;
private final Supplier<SurfaceControl.Transaction> mTransactionFactory;
private final Supplier<Boolean> mAreCornersRounded;
+ private final Supplier<Color> mColorSupplier;
+
private final Rect mOuter = new Rect();
private final Rect mInner = new Rect();
private final LetterboxSurface mTop = new LetterboxSurface("top");
@@ -64,10 +67,12 @@
*/
public Letterbox(Supplier<SurfaceControl.Builder> surfaceControlFactory,
Supplier<SurfaceControl.Transaction> transactionFactory,
- Supplier<Boolean> areCornersRounded) {
+ Supplier<Boolean> areCornersRounded,
+ Supplier<Color> colorSupplier) {
mSurfaceControlFactory = surfaceControlFactory;
mTransactionFactory = transactionFactory;
mAreCornersRounded = areCornersRounded;
+ mColorSupplier = colorSupplier;
}
/**
@@ -268,6 +273,7 @@
private final String mType;
private SurfaceControl mSurface;
+ private Color mColor;
private final Rect mSurfaceFrameRelative = new Rect();
private final Rect mLayoutFrameGlobal = new Rect();
@@ -292,9 +298,8 @@
.setColorLayer()
.setCallsite("LetterboxSurface.createSurface")
.build();
- t.setLayer(mSurface, -1)
- .setColor(mSurface, new float[]{0, 0, 0})
- .setColorSpaceAgnostic(mSurface, true);
+
+ t.setLayer(mSurface, -1).setColorSpaceAgnostic(mSurface, true);
}
void attachInput(WindowState win) {
@@ -344,6 +349,14 @@
if (mSurface == null) {
createSurface(t);
}
+
+ mColor = mColorSupplier.get();
+ final float[] rgbTmpFloat = new float[3];
+ rgbTmpFloat[0] = mColor.red();
+ rgbTmpFloat[1] = mColor.green();
+ rgbTmpFloat[2] = mColor.blue();
+ t.setColor(mSurface, rgbTmpFloat);
+
t.setPosition(mSurface, mSurfaceFrameRelative.left, mSurfaceFrameRelative.top);
t.setWindowCrop(mSurface, mSurfaceFrameRelative.width(),
mSurfaceFrameRelative.height());
@@ -358,7 +371,8 @@
}
public boolean needsApplySurfaceChanges() {
- return !mSurfaceFrameRelative.equals(mLayoutFrameRelative);
+ return !mSurfaceFrameRelative.equals(mLayoutFrameRelative)
+ || mColorSupplier.get() != mColor;
}
}
}
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/Task.java b/services/core/java/com/android/server/wm/Task.java
index f3f608b..57ba915 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -77,6 +77,7 @@
import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
@@ -144,6 +145,7 @@
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.TASK;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -2090,7 +2092,9 @@
td.setEnsureNavigationBarContrastWhenTransparent(
atd.getEnsureNavigationBarContrastWhenTransparent());
}
-
+ if (td.getBackgroundColorFloating() == 0) {
+ td.setBackgroundColorFloating(atd.getBackgroundColorFloating());
+ }
}
// End search once we get to root.
@@ -2878,6 +2882,16 @@
// In FULLSCREEN mode, always start with empty bounds to indicate "fill parent".
outBounds.setEmpty();
computeLetterboxBounds(outBounds, newParentConfig);
+ // Since the task is letterboxed due to mismatched orientation against its parent,
+ // sandbox max bounds to the app bounds.
+ if (!outBounds.isEmpty()) {
+ if (DEBUG_CONFIGURATION) {
+ ProtoLog.d(WM_DEBUG_CONFIGURATION, "Sandbox max bounds due to mismatched "
+ + "orientation with parent, to %s vs DisplayArea %s", outBounds,
+ getDisplayArea() != null ? getDisplayArea().getBounds() : "null");
+ }
+ getResolvedOverrideConfiguration().windowConfiguration.setMaxBounds(outBounds);
+ }
}
/** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 931f529..27b7fab 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -160,6 +160,7 @@
import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Point;
@@ -1010,10 +1011,30 @@
// Aspect ratio of task level letterboxing, values <= MIN_TASK_LETTERBOX_ASPECT_RATIO will be
// ignored.
- private float mTaskLetterboxAspectRatio;
+ private volatile float mTaskLetterboxAspectRatio;
+
+ /** Enum for Letterbox background type. */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({LETTERBOX_BACKGROUND_SOLID_COLOR, LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND,
+ LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING})
+ @interface LetterboxBackgroundType {};
+ /** Solid background using color specified in R.color.config_letterboxBackgroundColor. */
+ static final int LETTERBOX_BACKGROUND_SOLID_COLOR = 0;
+
+ /** Color specified in R.attr.colorBackground for the letterboxed application. */
+ static final int LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND = 1;
+
+ /** Color specified in R.attr.colorBackgroundFloating for the letterboxed application. */
+ static final int LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING = 2;
// Corners radius for activities presented in the letterbox mode, values < 0 will be ignored.
- private int mLetterboxActivityCornersRadius;
+ private volatile int mLetterboxActivityCornersRadius;
+
+ // Color for {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} letterbox background type.
+ private volatile Color mLetterboxBackgroundColor;
+
+ @LetterboxBackgroundType
+ private volatile int mLetterboxBackgroundType;
final InputManagerService mInputManager;
final DisplayManagerInternal mDisplayManagerInternal;
@@ -1240,10 +1261,15 @@
com.android.internal.R.bool.config_perDisplayFocusEnabled);
mAssistantOnTopOfDream = context.getResources().getBoolean(
com.android.internal.R.bool.config_assistantOnTopOfDream);
+
mTaskLetterboxAspectRatio = context.getResources().getFloat(
com.android.internal.R.dimen.config_taskLetterboxAspectRatio);
mLetterboxActivityCornersRadius = context.getResources().getInteger(
com.android.internal.R.integer.config_letterboxActivityCornersRadius);
+ mLetterboxBackgroundColor = Color.valueOf(context.getResources().getColor(
+ com.android.internal.R.color.config_letterboxBackgroundColor));
+ mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(context);
+
mInputManager = inputManager; // Must be before createDisplayContentLocked.
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
@@ -1652,7 +1678,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.
@@ -3901,14 +3927,7 @@
* the framework implementation will be used to determine the aspect ratio.
*/
void setTaskLetterboxAspectRatio(float aspectRatio) {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- mTaskLetterboxAspectRatio = aspectRatio;
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ mTaskLetterboxAspectRatio = aspectRatio;
}
/**
@@ -3916,29 +3935,15 @@
* com.android.internal.R.dimen.config_taskLetterboxAspectRatio}.
*/
void resetTaskLetterboxAspectRatio() {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- mTaskLetterboxAspectRatio = mContext.getResources().getFloat(
- com.android.internal.R.dimen.config_taskLetterboxAspectRatio);
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ mTaskLetterboxAspectRatio = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_taskLetterboxAspectRatio);
}
/**
* Gets the aspect ratio of task level letterboxing.
*/
float getTaskLetterboxAspectRatio() {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- return mTaskLetterboxAspectRatio;
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ return mTaskLetterboxAspectRatio;
}
/**
@@ -3948,14 +3953,7 @@
* and corners of the activity won't be rounded.
*/
void setLetterboxActivityCornersRadius(int cornersRadius) {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- mLetterboxActivityCornersRadius = cornersRadius;
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ mLetterboxActivityCornersRadius = cornersRadius;
}
/**
@@ -3963,15 +3961,8 @@
* com.android.internal.R.integer.config_letterboxActivityCornersRadius}.
*/
void resetLetterboxActivityCornersRadius() {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- mLetterboxActivityCornersRadius = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_letterboxActivityCornersRadius);
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ mLetterboxActivityCornersRadius = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_letterboxActivityCornersRadius);
}
/**
@@ -3985,14 +3976,67 @@
* Gets corners raidus for activities presented in the letterbox mode.
*/
int getLetterboxActivityCornersRadius() {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- return mLetterboxActivityCornersRadius;
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ return mLetterboxActivityCornersRadius;
+ }
+
+ /**
+ * Gets color of letterbox background which is used when {@link
+ * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
+ * fallback for other backfround types.
+ */
+ Color getLetterboxBackgroundColor() {
+ return mLetterboxBackgroundColor;
+ }
+
+
+ /**
+ * Sets color of letterbox background which is used when {@link
+ * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
+ * fallback for other backfround types.
+ */
+ void setLetterboxBackgroundColor(Color color) {
+ mLetterboxBackgroundColor = color;
+ }
+
+ /**
+ * Resets color of letterbox background to {@link
+ * com.android.internal.R.color.config_letterboxBackgroundColor}.
+ */
+ void resetLetterboxBackgroundColor() {
+ mLetterboxBackgroundColor = Color.valueOf(mContext.getResources().getColor(
+ com.android.internal.R.color.config_letterboxBackgroundColor));
+ }
+
+ /**
+ * Gets {@link LetterboxBackgroundType} specified in {@link
+ * com.android.internal.R.integer.config_letterboxBackgroundType} or over via ADB command.
+ */
+ @LetterboxBackgroundType
+ int getLetterboxBackgroundType() {
+ return mLetterboxBackgroundType;
+ }
+
+ /** Sets letterbox background type. */
+ void setLetterboxBackgroundType(@LetterboxBackgroundType int backgroundType) {
+ mLetterboxBackgroundType = backgroundType;
+ }
+
+ /**
+ * Resets cletterbox background type to {@link
+ * com.android.internal.R.integer.config_letterboxBackgroundType}.
+ */
+ void resetLetterboxBackgroundType() {
+ mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext);
+ }
+
+ @LetterboxBackgroundType
+ private static int readLetterboxBackgroundTypeFromConfig(Context context) {
+ int backgroundType = context.getResources().getInteger(
+ com.android.internal.R.integer.config_letterboxBackgroundType);
+ return backgroundType == LETTERBOX_BACKGROUND_SOLID_COLOR
+ || backgroundType == LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND
+ || backgroundType == LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING
+ ? backgroundType : LETTERBOX_BACKGROUND_SOLID_COLOR;
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index badd29a..645786c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -18,6 +18,11 @@
import static android.os.Build.IS_USER;
+import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_SOLID_COLOR;
+
+import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.ParcelFileDescriptor;
@@ -34,6 +39,7 @@
import com.android.internal.protolog.ProtoLogImpl;
import com.android.server.LocalServices;
import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.wm.WindowManagerService.LetterboxBackgroundType;
import java.io.IOException;
import java.io.PrintWriter;
@@ -119,6 +125,14 @@
return runSetLetterboxActivityCornersRadius(pw);
case "get-letterbox-activity-corners-radius":
return runGetLetterboxActivityCornersRadius(pw);
+ case "set-letterbox-background-type":
+ return runSetLetterboxBackgroundType(pw);
+ case "get-letterbox-background-type":
+ return runGetLetterboxBackgroundType(pw);
+ case "set-letterbox-background-color":
+ return runSetLetterboxBackgroundColor(pw);
+ case "get-letterbox-background-color":
+ return runGetLetterboxBackgroundColor(pw);
case "reset":
return runReset(pw);
default:
@@ -581,6 +595,79 @@
return 0;
}
+ private int runSetLetterboxBackgroundType(PrintWriter pw) throws RemoteException {
+ @LetterboxBackgroundType final int backgroundType;
+
+ String arg = getNextArgRequired();
+ if ("reset".equals(arg)) {
+ mInternal.resetLetterboxBackgroundType();
+ return 0;
+ }
+ switch (arg) {
+ case "solid_color":
+ backgroundType = LETTERBOX_BACKGROUND_SOLID_COLOR;
+ break;
+ case "app_color_background":
+ backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+ break;
+ case "app_color_background_floating":
+ backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+ break;
+ default:
+ getErrPrintWriter().println(
+ "Error: 'reset', 'solid_color' or 'app_color_background' should "
+ + "be provided as an argument");
+ return -1;
+ }
+
+ mInternal.setLetterboxBackgroundType(backgroundType);
+ return 0;
+ }
+
+ private int runGetLetterboxBackgroundType(PrintWriter pw) throws RemoteException {
+ @LetterboxBackgroundType final int backgroundType = mInternal.getLetterboxBackgroundType();
+ switch (backgroundType) {
+ case LETTERBOX_BACKGROUND_SOLID_COLOR:
+ pw.println("Letterbox background type is 'solid_color'");
+ break;
+ case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
+ pw.println("Letterbox background type is 'app_color_background'");
+ break;
+ case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
+ pw.println("Letterbox background type is 'app_color_background_floating'");
+ break;
+ default:
+ throw new AssertionError("Unexpected letterbox background type: " + backgroundType);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException {
+ final Color color;
+ String arg = getNextArgRequired();
+ try {
+ if ("reset".equals(arg)) {
+ mInternal.resetLetterboxBackgroundColor();
+ return 0;
+ }
+ color = Color.valueOf(Color.parseColor(arg));
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'reset' or color in #RRGGBB format should be provided as "
+ + "an argument " + e + " but got " + arg);
+ return -1;
+ }
+
+ mInternal.setLetterboxBackgroundColor(color);
+ return 0;
+ }
+
+ private int runGetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException {
+ final Color color = mInternal.getLetterboxBackgroundColor();
+ pw.println("Letterbox background color is " + Integer.toHexString(color.toArgb()));
+ return 0;
+ }
+
private int runReset(PrintWriter pw) throws RemoteException {
int displayId = getDisplayId(getNextArg());
@@ -611,6 +698,12 @@
// set-letterbox-activity-corners-radius
mInternal.resetLetterboxActivityCornersRadius();
+ // set-letterbox-background-type
+ mInternal.resetLetterboxBackgroundType();
+
+ // set-letterbox-background-color
+ mInternal.resetLetterboxBackgroundColor();
+
pw.println("Reset all settings for displayId=" + displayId);
return 0;
}
@@ -652,6 +745,16 @@
pw.println(" Corners radius for activities in the letterbox mode. If radius < 0,");
pw.println(" both it and R.integer.config_letterboxActivityCornersRadius will be");
pw.println(" ignored and corners of the activity won't be rounded.");
+ pw.println(" set-letterbox-background-color [reset|colorName|'\\#RRGGBB']");
+ pw.println(" get-letterbox-background-color");
+ pw.println(" Color of letterbox background which is be used when letterbox background");
+ pw.println(" type is 'solid-color'. Use get(set)-letterbox-background-type to check");
+ pw.println(" and control letterbox background type. See Color#parseColor for allowed");
+ pw.println(" color formats (#RRGGBB and some colors by name, e.g. magenta or olive). ");
+ pw.println(" set-letterbox-background-type [reset|solid_color|app_color_background");
+ pw.println(" |app_color_background_floating]");
+ pw.println(" get-letterbox-background-type");
+ pw.println(" Type of background used in the letterbox mode.");
pw.println(" reset [-d DISPLAY_ID]");
pw.println(" Reset all override settings.");
if (!IS_USER) {
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/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
index 15bc93e..8b2beb2 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
@@ -43,10 +43,14 @@
@GuardedBy("mLock")
private final SparseIntArray mPasswordQuality = new SparseIntArray();
+ @GuardedBy("mLock")
+ private final SparseIntArray mPermissionPolicy = new SparseIntArray();
+
public void onUserRemoved(int userHandle) {
synchronized (mLock) {
mScreenCaptureDisabled.delete(userHandle);
mPasswordQuality.delete(userHandle);
+ mPermissionPolicy.delete(userHandle);
}
}
@@ -78,12 +82,28 @@
}
}
+ @Override
+ public int getPermissionPolicy(@UserIdInt int userHandle) {
+ synchronized (mLock) {
+ return mPermissionPolicy.get(userHandle,
+ DevicePolicyManager.PERMISSION_POLICY_PROMPT);
+ }
+ }
+
+ /** Update the permission policy for the given user. */
+ public void setPermissionPolicy(@UserIdInt int userHandle, int policy) {
+ synchronized (mLock) {
+ mPermissionPolicy.put(userHandle, policy);
+ }
+ }
+
/** Dump content */
public void dump(IndentingPrintWriter pw) {
pw.println("Device policy cache:");
pw.increaseIndent();
pw.println("Screen capture disabled: " + mScreenCaptureDisabled.toString());
pw.println("Password quality: " + mPasswordQuality.toString());
+ pw.println("Permission policy: " + mPermissionPolicy.toString());
pw.decreaseIndent();
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c3bb757..d235a7f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -35,10 +35,8 @@
import static android.app.admin.DevicePolicyManager.CODE_MANAGED_USERS_NOT_SUPPORTED;
import static android.app.admin.DevicePolicyManager.CODE_NONSYSTEM_USER_EXISTS;
import static android.app.admin.DevicePolicyManager.CODE_NOT_SYSTEM_USER;
-import static android.app.admin.DevicePolicyManager.CODE_NOT_SYSTEM_USER_SPLIT;
import static android.app.admin.DevicePolicyManager.CODE_OK;
import static android.app.admin.DevicePolicyManager.CODE_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS;
-import static android.app.admin.DevicePolicyManager.CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER;
import static android.app.admin.DevicePolicyManager.CODE_SYSTEM_USER;
import static android.app.admin.DevicePolicyManager.CODE_USER_HAS_PROFILE_OWNER;
import static android.app.admin.DevicePolicyManager.CODE_USER_NOT_RUNNING;
@@ -1380,11 +1378,6 @@
SystemProperties.set(key, value);
}
- // TODO (b/137101239): clean up split system user codes
- boolean userManagerIsSplitSystemUser() {
- return UserManager.isSplitSystemUser();
- }
-
boolean userManagerIsHeadlessSystemUserMode() {
return UserManager.isHeadlessSystemUserMode();
}
@@ -2942,6 +2935,7 @@
// reading the value during user switch, due to onStartUser() being asynchronous.
updatePasswordQualityCacheForUserGroup(
userId == UserHandle.USER_SYSTEM ? UserHandle.USER_ALL : userId);
+ updatePermissionPolicyCache(userId);
startOwnerService(userId, "start-user");
}
@@ -7404,48 +7398,18 @@
return mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) > 0;
}
+ // TODO (b/137101239): remove this method in follow-up CL
+ // since it's only used for split system user.
@Override
public void setForceEphemeralUsers(ComponentName who, boolean forceEphemeralUsers) {
- if (!mHasFeature) {
- return;
- }
- Objects.requireNonNull(who, "ComponentName is null");
- final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
-
- // Allow setting this policy to true only if there is a split system user.
- if (forceEphemeralUsers && !mInjector.userManagerIsSplitSystemUser()) {
- throw new UnsupportedOperationException(
- "Cannot force ephemeral users on systems without split system user.");
- }
- boolean removeAllUsers = false;
- synchronized (getLockObject()) {
- final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
- if (deviceOwner.forceEphemeralUsers != forceEphemeralUsers) {
- deviceOwner.forceEphemeralUsers = forceEphemeralUsers;
- saveSettingsLocked(caller.getUserId());
- mUserManagerInternal.setForceEphemeralUsers(forceEphemeralUsers);
- removeAllUsers = forceEphemeralUsers;
- }
- }
- if (removeAllUsers) {
- mInjector.binderWithCleanCallingIdentity(() -> mUserManagerInternal.removeAllUsers());
- }
+ throw new UnsupportedOperationException("This method was used by split system user only.");
}
+ // TODO (b/137101239): remove this method in follow-up CL
+ // since it's only used for split system user.
@Override
public boolean getForceEphemeralUsers(ComponentName who) {
- if (!mHasFeature) {
- return false;
- }
- Objects.requireNonNull(who, "ComponentName is null");
- final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
-
- synchronized (getLockObject()) {
- final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
- return deviceOwner.forceEphemeralUsers;
- }
+ throw new UnsupportedOperationException("This method was used by split system user only.");
}
@Override
@@ -12498,11 +12462,13 @@
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT)));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_PERMISSION_POLICY);
+ final int forUser = caller.getUserId();
synchronized (getLockObject()) {
- DevicePolicyData userPolicy = getUserData(caller.getUserId());
+ DevicePolicyData userPolicy = getUserData(forUser);
if (userPolicy.mPermissionPolicy != policy) {
userPolicy.mPermissionPolicy = policy;
- saveSettingsLocked(caller.getUserId());
+ mPolicyCache.setPermissionPolicy(forUser, policy);
+ saveSettingsLocked(forUser);
}
}
DevicePolicyEventLogger
@@ -12513,13 +12479,17 @@
.write();
}
+ private void updatePermissionPolicyCache(int userId) {
+ synchronized (getLockObject()) {
+ DevicePolicyData userPolicy = getUserData(userId);
+ mPolicyCache.setPermissionPolicy(userId, userPolicy.mPermissionPolicy);
+ }
+ }
+
@Override
public int getPermissionPolicy(ComponentName admin) throws RemoteException {
int userId = UserHandle.getCallingUserId();
- synchronized (getLockObject()) {
- DevicePolicyData userPolicy = getUserData(userId);
- return userPolicy.mPermissionPolicy;
- }
+ return mPolicyCache.getPermissionPolicy(userId);
}
@Override
@@ -12733,13 +12703,6 @@
case DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE:
case DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE:
return checkDeviceOwnerProvisioningPreCondition(callingUserId);
- // TODO (b/137101239): clean up split system user codes
- // ACTION_PROVISION_MANAGED_USER and ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE
- // only supported on split-user systems.
- case DevicePolicyManager.ACTION_PROVISION_MANAGED_USER:
- return checkManagedUserProvisioningPreCondition(callingUserId);
- case DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE:
- return checkManagedShareableDeviceProvisioningPreCondition(callingUserId);
}
}
throw new IllegalArgumentException("Unknown provisioning action " + action);
@@ -12775,14 +12738,12 @@
}
}
- // TODO (b/137101239): clean up split system user codes
if (isAdb) {
// If shell command runs after user setup completed check device status. Otherwise, OK.
if (mIsWatch || hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
// In non-headless system user mode, DO can be setup only if
// there's no non-system user
if (!mInjector.userManagerIsHeadlessSystemUserMode()
- && !mInjector.userManagerIsSplitSystemUser()
&& mUserManager.getUserCount() > 1) {
return CODE_NONSYSTEM_USER_EXISTS;
}
@@ -12801,16 +12762,13 @@
}
return CODE_OK;
} else {
- if (!mInjector.userManagerIsSplitSystemUser()) {
- // In non-split user mode, DO has to be user 0
- if (deviceOwnerUserId != UserHandle.USER_SYSTEM) {
- return CODE_NOT_SYSTEM_USER;
- }
- // Only provision DO before setup wizard completes
- // TODO (b/171423186): implement deferred DO setup for headless system user mode
- if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
- return CODE_USER_SETUP_COMPLETED;
- }
+ // DO has to be user 0
+ if (deviceOwnerUserId != UserHandle.USER_SYSTEM) {
+ return CODE_NOT_SYSTEM_USER;
+ }
+ // Only provision DO before setup wizard completes
+ if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
+ return CODE_USER_SETUP_COMPLETED;
}
return CODE_OK;
}
@@ -12831,17 +12789,11 @@
}
}
- // TODO (b/137101239): clean up split system user codes
private int checkManagedProfileProvisioningPreCondition(String packageName,
@UserIdInt int callingUserId) {
if (!hasFeatureManagedUsers()) {
return CODE_MANAGED_USERS_NOT_SUPPORTED;
}
- if (callingUserId == UserHandle.USER_SYSTEM
- && mInjector.userManagerIsSplitSystemUser()) {
- // Managed-profiles cannot be setup on the system user.
- return CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER;
- }
if (getProfileOwnerAsUser(callingUserId) != null) {
// Managed user cannot have a managed profile.
return CODE_USER_HAS_PROFILE_OWNER;
@@ -12917,37 +12869,6 @@
return null;
}
- // TODO (b/137101239): clean up split system user codes
- private int checkManagedUserProvisioningPreCondition(int callingUserId) {
- if (!hasFeatureManagedUsers()) {
- return CODE_MANAGED_USERS_NOT_SUPPORTED;
- }
- if (!mInjector.userManagerIsSplitSystemUser()) {
- // ACTION_PROVISION_MANAGED_USER only supported on split-user systems.
- return CODE_NOT_SYSTEM_USER_SPLIT;
- }
- if (callingUserId == UserHandle.USER_SYSTEM) {
- // System user cannot be a managed user.
- return CODE_SYSTEM_USER;
- }
- if (hasUserSetupCompleted(callingUserId)) {
- return CODE_USER_SETUP_COMPLETED;
- }
- if (mIsWatch && hasPaired(UserHandle.USER_SYSTEM)) {
- return CODE_HAS_PAIRED;
- }
- return CODE_OK;
- }
-
- // TODO (b/137101239): clean up split system user codes
- private int checkManagedShareableDeviceProvisioningPreCondition(int callingUserId) {
- if (!mInjector.userManagerIsSplitSystemUser()) {
- // ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE only supported on split-user systems.
- return CODE_NOT_SYSTEM_USER_SPLIT;
- }
- return checkDeviceOwnerProvisioningPreCondition(callingUserId);
- }
-
private boolean hasFeatureManagedUsers() {
try {
return mIPackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0);
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/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 9b390db..636be4a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1344,6 +1344,13 @@
mSystemServiceManager.startService(DropBoxManagerService.class);
t.traceEnd();
+ // Grants default permissions and defines roles
+ t.traceBegin("StartRoleManagerService");
+ LocalManagerRegistry.addManager(RoleServicePlatformHelper.class,
+ new RoleServicePlatformHelperImpl(mSystemContext));
+ mSystemServiceManager.startService(ROLE_SERVICE_CLASS);
+ t.traceEnd();
+
t.traceBegin("StartVibratorManagerService");
mSystemServiceManager.startService(VibratorManagerService.Lifecycle.class);
t.traceEnd();
@@ -2060,13 +2067,6 @@
t.traceEnd();
}
- // Grants default permissions and defines roles
- t.traceBegin("StartRoleManagerService");
- LocalManagerRegistry.addManager(RoleServicePlatformHelper.class,
- new RoleServicePlatformHelperImpl(mSystemContext));
- mSystemServiceManager.startService(ROLE_SERVICE_CLASS);
- t.traceEnd();
-
// We need to always start this service, regardless of whether the
// FEATURE_VOICE_RECOGNIZERS feature is set, because it needs to take care
// of initializing various settings. It will internally modify its behavior
diff --git a/services/smartspace/OWNERS b/services/smartspace/OWNERS
new file mode 100644
index 0000000..19ef9d7
--- /dev/null
+++ b/services/smartspace/OWNERS
@@ -0,0 +1,2 @@
+srazdan@google.com
+alexmang@google.com
\ No newline at end of file
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/job/WorkCountTrackerTest.java b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
index 15a9bcf..6208801 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
@@ -21,9 +21,11 @@
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
-import android.util.Log;
+import android.annotation.NonNull;
import android.util.Pair;
+import android.util.SparseIntArray;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
@@ -58,106 +60,112 @@
* Represents running and pending jobs.
*/
class Jobs {
- public int runningFg;
- public int runningBg;
- public int pendingFg;
- public int pendingBg;
+ public final SparseIntArray running = new SparseIntArray();
+ public final SparseIntArray pending = new SparseIntArray();
public void maybeEnqueueJobs(double startRatio, double fgJobRatio) {
while (mRandom.nextDouble() < startRatio) {
if (mRandom.nextDouble() < fgJobRatio) {
- pendingFg++;
+ pending.put(WORK_TYPE_TOP, pending.get(WORK_TYPE_TOP) + 1);
} else {
- pendingBg++;
+ pending.put(WORK_TYPE_BG, pending.get(WORK_TYPE_BG) + 1);
}
}
}
public void maybeFinishJobs(double stopRatio) {
- for (int i = runningBg; i > 0; i--) {
+ for (int i = running.get(WORK_TYPE_BG); i > 0; i--) {
if (mRandom.nextDouble() < stopRatio) {
- runningBg--;
+ running.put(WORK_TYPE_BG, running.get(WORK_TYPE_BG) - 1);
}
}
- for (int i = runningFg; i > 0; i--) {
+ for (int i = running.get(WORK_TYPE_TOP); i > 0; i--) {
if (mRandom.nextDouble() < stopRatio) {
- runningFg--;
+ running.put(WORK_TYPE_TOP, running.get(WORK_TYPE_TOP) - 1);
}
}
}
}
- private void startPendingJobs(Jobs jobs, int totalMax, int maxBg, int minBg) {
- mWorkCountTracker.setConfig(new JobConcurrencyManager.WorkTypeConfig("critical",
- totalMax,
- // defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, totalMax - maxBg),
- Pair.create(WORK_TYPE_BG, minBg)),
- // defaultMax
- List.of(Pair.create(WORK_TYPE_BG, maxBg))));
+ private void startPendingJobs(Jobs jobs, int totalMax,
+ @NonNull List<Pair<Integer, Integer>> minLimits,
+ @NonNull List<Pair<Integer, Integer>> maxLimits) {
+ mWorkCountTracker.setConfig(new JobConcurrencyManager.WorkTypeConfig(
+ "test", totalMax, minLimits, maxLimits));
mWorkCountTracker.resetCounts();
- for (int i = 0; i < jobs.runningFg; i++) {
- mWorkCountTracker.incrementRunningJobCount(WORK_TYPE_TOP);
- }
- for (int i = 0; i < jobs.runningBg; i++) {
- mWorkCountTracker.incrementRunningJobCount(WORK_TYPE_BG);
- }
+ for (int i = 0; i < jobs.running.size(); ++i) {
+ final int workType = jobs.running.keyAt(i);
+ final int count = jobs.running.valueAt(i);
- for (int i = 0; i < jobs.pendingFg; i++) {
- mWorkCountTracker.incrementPendingJobCount(WORK_TYPE_TOP);
+ for (int c = 0; c < count; ++c) {
+ mWorkCountTracker.incrementRunningJobCount(workType);
+ }
}
- for (int i = 0; i < jobs.pendingBg; i++) {
- mWorkCountTracker.incrementPendingJobCount(WORK_TYPE_BG);
+ for (int i = 0; i < jobs.pending.size(); ++i) {
+ final int workType = jobs.pending.keyAt(i);
+ final int count = jobs.pending.valueAt(i);
+
+ for (int c = 0; c < count; ++c) {
+ mWorkCountTracker.incrementPendingJobCount(workType);
+ }
}
mWorkCountTracker.onCountDone();
- while ((jobs.pendingFg > 0
+ while ((jobs.pending.get(WORK_TYPE_TOP) > 0
&& mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE)
- || (jobs.pendingBg > 0
+ || (jobs.pending.get(WORK_TYPE_BG) > 0
&& mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE)) {
final boolean isStartingFg = mRandom.nextBoolean();
if (isStartingFg) {
- if (jobs.pendingFg > 0
+ if (jobs.pending.get(WORK_TYPE_TOP) > 0
&& mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE) {
- jobs.pendingFg--;
- jobs.runningFg++;
+ jobs.pending.put(WORK_TYPE_TOP, jobs.pending.get(WORK_TYPE_TOP) - 1);
+ jobs.running.put(WORK_TYPE_TOP, jobs.running.get(WORK_TYPE_TOP) + 1);
mWorkCountTracker.stageJob(WORK_TYPE_TOP);
mWorkCountTracker.onJobStarted(WORK_TYPE_TOP);
}
} else {
- if (jobs.pendingBg > 0
+ if (jobs.pending.get(WORK_TYPE_BG) > 0
&& mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE) {
- jobs.pendingBg--;
- jobs.runningBg++;
+ jobs.pending.put(WORK_TYPE_BG, jobs.pending.get(WORK_TYPE_BG) - 1);
+ jobs.running.put(WORK_TYPE_BG, jobs.running.get(WORK_TYPE_BG) + 1);
mWorkCountTracker.stageJob(WORK_TYPE_BG);
mWorkCountTracker.onJobStarted(WORK_TYPE_BG);
}
}
}
-
- Log.i(TAG, "" + mWorkCountTracker);
}
/**
* Used by the following testRandom* tests.
*/
- private void checkRandom(Jobs jobs, int numTests, int totalMax, int maxBg, int minBg,
+ private void checkRandom(Jobs jobs, int numTests, int totalMax,
+ @NonNull List<Pair<Integer, Integer>> minLimits,
+ @NonNull List<Pair<Integer, Integer>> maxLimits,
double startRatio, double fgJobRatio, double stopRatio) {
for (int i = 0; i < numTests; i++) {
-
jobs.maybeFinishJobs(stopRatio);
jobs.maybeEnqueueJobs(startRatio, fgJobRatio);
- startPendingJobs(jobs, totalMax, maxBg, minBg);
+ startPendingJobs(jobs, totalMax, minLimits, maxLimits);
- assertThat(jobs.runningFg).isAtMost(totalMax);
- assertThat(jobs.runningBg).isAtMost(totalMax);
- assertThat(jobs.runningFg + jobs.runningBg).isAtMost(totalMax);
- assertThat(jobs.runningBg).isAtMost(maxBg);
+ int totalRunning = 0;
+ for (int r = 0; r < jobs.running.size(); ++r) {
+ final int numRunning = jobs.running.valueAt(r);
+ assertWithMessage(
+ "Work type " + jobs.running.keyAt(r) + " is running too many jobs")
+ .that(numRunning).isAtMost(totalMax);
+ totalRunning += numRunning;
+ }
+ assertThat(totalRunning).isAtMost(totalMax);
+ for (Pair<Integer, Integer> maxLimit : maxLimits) {
+ assertWithMessage("Work type " + maxLimit.first + " is running too many jobs")
+ .that(jobs.running.get(maxLimit.first)).isAtMost(maxLimit.second);
+ }
}
}
@@ -170,13 +178,14 @@
final int numTests = 5000;
final int totalMax = 6;
- final int maxBg = 4;
- final int minBg = 2;
+ final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+ final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double stopRatio = 0.1;
final double fgJobRatio = 0.5;
final double startRatio = 0.1;
- checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio , stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
+ startRatio, fgJobRatio, stopRatio);
}
@Test
@@ -185,13 +194,14 @@
final int numTests = 5000;
final int totalMax = 2;
- final int maxBg = 2;
- final int minBg = 0;
+ final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
+ final List<Pair<Integer, Integer>> minLimits = List.of();
final double stopRatio = 0.5;
final double fgJobRatio = 0.5;
final double startRatio = 0.5;
- checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
+ startRatio, fgJobRatio, stopRatio);
}
@Test
@@ -200,13 +210,14 @@
final int numTests = 5000;
final int totalMax = 2;
- final int maxBg = 2;
- final int minBg = 2;
+ final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
+ final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double stopRatio = 0.5;
final double fgJobRatio = 0.5;
final double startRatio = 0.5;
- checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
+ startRatio, fgJobRatio, stopRatio);
}
@Test
@@ -215,13 +226,14 @@
final int numTests = 5000;
final int totalMax = 10;
- final int maxBg = 2;
- final int minBg = 0;
+ final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
+ final List<Pair<Integer, Integer>> minLimits = List.of();
final double stopRatio = 0.5;
final double fgJobRatio = 0.5;
final double startRatio = 0.5;
- checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
+ startRatio, fgJobRatio, stopRatio);
}
@Test
@@ -230,13 +242,14 @@
final int numTests = 5000;
final int totalMax = 6;
- final int maxBg = 4;
- final int minBg = 2;
+ final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+ final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double stopRatio = 0.5;
final double fgJobRatio = 0.1;
final double startRatio = 0.5;
- checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
+ startRatio, fgJobRatio, stopRatio);
}
@Test
@@ -245,13 +258,14 @@
final int numTests = 5000;
final int totalMax = 6;
- final int maxBg = 4;
- final int minBg = 2;
+ final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+ final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double stopRatio = 0.5;
final double fgJobRatio = 0.9;
final double startRatio = 0.5;
- checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
+ startRatio, fgJobRatio, stopRatio);
}
@Test
@@ -260,13 +274,14 @@
final int numTests = 5000;
final int totalMax = 6;
- final int maxBg = 4;
- final int minBg = 2;
+ final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+ final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double stopRatio = 0.4;
final double fgJobRatio = 0.1;
final double startRatio = 0.5;
- checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
+ startRatio, fgJobRatio, stopRatio);
}
@Test
@@ -275,53 +290,135 @@
final int numTests = 5000;
final int totalMax = 6;
- final int maxBg = 4;
- final int minBg = 2;
+ final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+ final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double stopRatio = 0.4;
final double fgJobRatio = 0.9;
final double startRatio = 0.5;
- checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
+ startRatio, fgJobRatio, stopRatio);
}
/** Used by the following tests */
- private void checkSimple(int totalMax, int maxBg, int minBg,
- int runningFg, int runningBg, int pendingFg, int pendingBg,
- int resultRunningFg, int resultRunningBg, int resultPendingFg, int resultPendingBg) {
+ private void checkSimple(int totalMax,
+ @NonNull List<Pair<Integer, Integer>> minLimits,
+ @NonNull List<Pair<Integer, Integer>> maxLimits,
+ @NonNull List<Pair<Integer, Integer>> running,
+ @NonNull List<Pair<Integer, Integer>> pending,
+ @NonNull List<Pair<Integer, Integer>> resultRunning,
+ @NonNull List<Pair<Integer, Integer>> resultPending) {
final Jobs jobs = new Jobs();
- jobs.runningFg = runningFg;
- jobs.runningBg = runningBg;
- jobs.pendingFg = pendingFg;
- jobs.pendingBg = pendingBg;
+ for (Pair<Integer, Integer> run : running) {
+ jobs.running.put(run.first, run.second);
+ }
+ for (Pair<Integer, Integer> pend : pending) {
+ jobs.pending.put(pend.first, pend.second);
+ }
- startPendingJobs(jobs, totalMax, maxBg, minBg);
+ startPendingJobs(jobs, totalMax, minLimits, maxLimits);
-// fail(mWorkerCountTracker.toString());
- assertThat(jobs.runningFg).isEqualTo(resultRunningFg);
- assertThat(jobs.runningBg).isEqualTo(resultRunningBg);
-
- assertThat(jobs.pendingFg).isEqualTo(resultPendingFg);
- assertThat(jobs.pendingBg).isEqualTo(resultPendingBg);
+ for (Pair<Integer, Integer> run : resultRunning) {
+ assertWithMessage("Incorrect running result for work type " + run.first)
+ .that(jobs.running.get(run.first)).isEqualTo(run.second);
+ }
+ for (Pair<Integer, Integer> pend : resultPending) {
+ assertWithMessage("Incorrect pending result for work type " + pend.first)
+ .that(jobs.pending.get(pend.first)).isEqualTo(pend.second);
+ }
}
-
@Test
public void testBasic() {
- // Args are:
- // First 3: Total-max, bg-max, bg-min.
- // Next 2: Running FG / BG
- // Next 2: Pending FG / BG
- // Next 4: Result running FG / BG, pending FG/BG.
- checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 1, 0, /*res run/pen=*/ 1, 0, 0, 0);
+ checkSimple(6,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+ /* run */ List.of(),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 1)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 1)),
+ /* resPen */ List.of());
- checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 0, /*res run/pen=*/ 6, 0, 4, 0);
+ checkSimple(6,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+ /* run */ List.of(),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 4)));
// When there are BG jobs pending, 2 (min-BG) jobs should run.
- checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 1, /*res run/pen=*/ 5, 1, 5, 0);
- checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 3, /*res run/pen=*/ 4, 2, 6, 1);
+ checkSimple(6,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+ /* run */ List.of(),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 1)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 5)));
+ checkSimple(6,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+ /* run */ List.of(),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 1)));
- checkSimple(8, 6, 2, /*run=*/ 0, 0, /*pen=*/ 0, 49, /*res run/pen=*/ 0, 6, 0, 43);
+ checkSimple(8,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 6)),
+ /* run */ List.of(),
+ /* pen */ List.of(Pair.create(WORK_TYPE_BG, 49)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_BG, 6)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_BG, 43)));
- checkSimple(6, 4, 2, /*run=*/ 6, 0, /*pen=*/ 10, 3, /*res run/pen=*/ 6, 0, 10, 3);
+ checkSimple(8,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 6)),
+ /* run */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 4)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_BG, 49)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 6)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_BG, 47)));
+
+ checkSimple(8,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 6)),
+ /* run */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 4)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 49)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 4)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 8), Pair.create(WORK_TYPE_BG, 49)));
+
+ checkSimple(8,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 6)),
+ /* run */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 1)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 49)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 48)));
+
+ checkSimple(8,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 1)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)),
+ /* run */ List.of(Pair.create(WORK_TYPE_BG, 6)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 49)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 6)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 8), Pair.create(WORK_TYPE_BG, 49)));
+
+ checkSimple(6,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+ /* run */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)));
+
+ // This could happen if we lower the effective config due to higher memory pressure after
+ // we've already started running jobs. We shouldn't stop already running jobs, but also
+ // shouldn't start new ones.
+ checkSimple(5,
+ /* min */ List.of(),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 1)),
+ /* run */ List.of(Pair.create(WORK_TYPE_BG, 6)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_BG, 6)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)));
}
}
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/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
index c59ead9..6dcfb42 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -1123,10 +1123,10 @@
}
/**
- * A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving
- * like the real thing should, it also asserts preconditions.
+ * A fake implementation of {@link TimeDetectorStrategyImpl.Environment}. Besides tracking
+ * changes and behaving like the real thing should, it also asserts preconditions.
*/
- private static class FakeCallback implements TimeDetectorStrategyImpl.Callback {
+ private static class FakeEnvironment implements TimeDetectorStrategyImpl.Environment {
private boolean mAutoTimeDetectionEnabled;
private boolean mWakeLockAcquired;
private long mElapsedRealtimeMillis;
@@ -1254,41 +1254,41 @@
*/
private class Script {
- private final FakeCallback mFakeCallback;
+ private final FakeEnvironment mFakeEnvironment;
private final TimeDetectorStrategyImpl mTimeDetectorStrategy;
Script() {
- mFakeCallback = new FakeCallback();
- mTimeDetectorStrategy = new TimeDetectorStrategyImpl(mFakeCallback);
+ mFakeEnvironment = new FakeEnvironment();
+ mTimeDetectorStrategy = new TimeDetectorStrategyImpl(mFakeEnvironment);
}
Script pokeAutoTimeDetectionEnabled(boolean enabled) {
- mFakeCallback.pokeAutoTimeDetectionEnabled(enabled);
+ mFakeEnvironment.pokeAutoTimeDetectionEnabled(enabled);
return this;
}
Script pokeFakeClocks(TimestampedValue<Instant> timeInfo) {
- mFakeCallback.pokeElapsedRealtimeMillis(timeInfo.getReferenceTimeMillis());
- mFakeCallback.pokeSystemClockMillis(timeInfo.getValue().toEpochMilli());
+ mFakeEnvironment.pokeElapsedRealtimeMillis(timeInfo.getReferenceTimeMillis());
+ mFakeEnvironment.pokeSystemClockMillis(timeInfo.getValue().toEpochMilli());
return this;
}
Script pokeThresholds(int systemClockUpdateThreshold) {
- mFakeCallback.pokeSystemClockUpdateThreshold(systemClockUpdateThreshold);
+ mFakeEnvironment.pokeSystemClockUpdateThreshold(systemClockUpdateThreshold);
return this;
}
Script pokeAutoOriginPriorities(@Origin int... autoOriginPriorities) {
- mFakeCallback.pokeAutoOriginPriorities(autoOriginPriorities);
+ mFakeEnvironment.pokeAutoOriginPriorities(autoOriginPriorities);
return this;
}
long peekElapsedRealtimeMillis() {
- return mFakeCallback.peekElapsedRealtimeMillis();
+ return mFakeEnvironment.peekElapsedRealtimeMillis();
}
long peekSystemClockMillis() {
- return mFakeCallback.peekSystemClockMillis();
+ return mFakeEnvironment.peekSystemClockMillis();
}
Script simulateTelephonyTimeSuggestion(TelephonyTimeSuggestion timeSuggestion) {
@@ -1324,13 +1324,13 @@
}
Script simulateAutoTimeDetectionToggle() {
- mFakeCallback.simulateAutoTimeZoneDetectionToggle();
+ mFakeEnvironment.simulateAutoTimeZoneDetectionToggle();
mTimeDetectorStrategy.handleAutoTimeConfigChanged();
return this;
}
Script simulateTimePassing(long clockIncrementMillis) {
- mFakeCallback.simulateTimePassing(clockIncrementMillis);
+ mFakeEnvironment.simulateTimePassing(clockIncrementMillis);
return this;
}
@@ -1342,14 +1342,14 @@
}
Script verifySystemClockWasNotSetAndResetCallTracking() {
- mFakeCallback.verifySystemClockNotSet();
- mFakeCallback.resetCallTracking();
+ mFakeEnvironment.verifySystemClockNotSet();
+ mFakeEnvironment.resetCallTracking();
return this;
}
Script verifySystemClockWasSetAndResetCallTracking(long expectedSystemClockMillis) {
- mFakeCallback.verifySystemClockWasSet(expectedSystemClockMillis);
- mFakeCallback.resetCallTracking();
+ mFakeEnvironment.verifySystemClockWasSet(expectedSystemClockMillis);
+ mFakeEnvironment.resetCallTracking();
return this;
}
@@ -1427,7 +1427,7 @@
ManualTimeSuggestion generateManualTimeSuggestion(Instant suggestedTime) {
TimestampedValue<Long> utcTime =
new TimestampedValue<>(
- mFakeCallback.peekElapsedRealtimeMillis(),
+ mFakeEnvironment.peekElapsedRealtimeMillis(),
suggestedTime.toEpochMilli());
return new ManualTimeSuggestion(utcTime);
}
@@ -1461,7 +1461,7 @@
NetworkTimeSuggestion generateNetworkTimeSuggestion(Instant suggestedTime) {
TimestampedValue<Long> utcTime =
new TimestampedValue<>(
- mFakeCallback.peekElapsedRealtimeMillis(),
+ mFakeEnvironment.peekElapsedRealtimeMillis(),
suggestedTime.toEpochMilli());
return new NetworkTimeSuggestion(utcTime);
}
@@ -1473,7 +1473,7 @@
GnssTimeSuggestion generateGnssTimeSuggestion(Instant suggestedTime) {
TimestampedValue<Long> utcTime =
new TimestampedValue<>(
- mFakeCallback.peekElapsedRealtimeMillis(),
+ mFakeEnvironment.peekElapsedRealtimeMillis(),
suggestedTime.toEpochMilli());
return new GnssTimeSuggestion(utcTime);
}
@@ -1485,7 +1485,7 @@
ExternalTimeSuggestion generateExternalTimeSuggestion(Instant suggestedTime) {
TimestampedValue<Long> utcTime =
new TimestampedValue<>(
- mFakeCallback.peekElapsedRealtimeMillis(),
+ mFakeEnvironment.peekElapsedRealtimeMillis(),
suggestedTime.toEpochMilli());
return new ExternalTimeSuggestion(utcTime);
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index a6ffd20..c8dba5f 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -167,15 +167,15 @@
createConfig(null, false /* geoDetection */);
private TimeZoneDetectorStrategyImpl mTimeZoneDetectorStrategy;
- private FakeCallback mFakeCallback;
+ private FakeEnvironment mFakeEnvironment;
private MockConfigChangeListener mMockConfigChangeListener;
@Before
public void setUp() {
- mFakeCallback = new FakeCallback();
+ mFakeEnvironment = new FakeEnvironment();
mMockConfigChangeListener = new MockConfigChangeListener();
- mTimeZoneDetectorStrategy = new TimeZoneDetectorStrategyImpl(mFakeCallback);
+ mTimeZoneDetectorStrategy = new TimeZoneDetectorStrategyImpl(mFakeEnvironment);
mTimeZoneDetectorStrategy.addConfigChangeListener(mMockConfigChangeListener);
}
@@ -183,7 +183,7 @@
public void testGetCurrentUserConfiguration() {
new Script().initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED);
ConfigurationInternal expectedConfiguration =
- mFakeCallback.getConfigurationInternal(USER_ID);
+ mFakeEnvironment.getConfigurationInternal(USER_ID);
assertEquals(expectedConfiguration,
mTimeZoneDetectorStrategy.getCurrentUserConfigurationInternal());
}
@@ -490,7 +490,7 @@
private void makeSlotIndex1SuggestionAndCheckState(Script script, TelephonyTestCase testCase) {
// Give the next suggestion a different zone from the currently set device time zone;
- String currentZoneId = mFakeCallback.getDeviceTimeZone();
+ String currentZoneId = mFakeEnvironment.getDeviceTimeZone();
String suggestionZoneId =
"Europe/London".equals(currentZoneId) ? "Europe/Paris" : "Europe/London";
TelephonyTimeZoneSuggestion zoneSlotIndex1Suggestion =
@@ -610,9 +610,9 @@
}
/**
- * The {@link TimeZoneDetectorStrategyImpl.Callback} is left to detect whether changing the time
- * zone is actually necessary. This test proves that the strategy doesn't assume it knows the
- * current settings.
+ * The {@link TimeZoneDetectorStrategyImpl.Environment} is left to detect whether changing the
+ * time zone is actually necessary. This test proves that the strategy doesn't assume it knows
+ * the current settings.
*/
@Test
public void testTelephonySuggestionStrategyDoesNotAssumeCurrentSetting_autoTelephony() {
@@ -958,7 +958,7 @@
return builder.build();
}
- static class FakeCallback implements TimeZoneDetectorStrategyImpl.Callback {
+ static class FakeEnvironment implements TimeZoneDetectorStrategyImpl.Environment {
private final TestState<ConfigurationInternal> mConfigurationInternal = new TestState<>();
private final TestState<String> mTimeZoneId = new TestState<>();
@@ -1051,12 +1051,12 @@
private class Script {
Script initializeConfig(ConfigurationInternal configuration) {
- mFakeCallback.initializeConfig(configuration);
+ mFakeEnvironment.initializeConfig(configuration);
return this;
}
Script initializeTimeZoneSetting(String zoneId) {
- mFakeCallback.initializeTimeZoneSetting(zoneId);
+ mFakeEnvironment.initializeTimeZoneSetting(zoneId);
return this;
}
@@ -1084,7 +1084,7 @@
Script simulateManualTimeZoneSuggestion(
@UserIdInt int userId, ManualTimeZoneSuggestion manualTimeZoneSuggestion,
boolean expectedResult) {
- mFakeCallback.assertKnownUser(userId);
+ mFakeEnvironment.assertKnownUser(userId);
boolean actualResult = mTimeZoneDetectorStrategy.suggestManualTimeZone(
userId, manualTimeZoneSuggestion);
assertEquals(expectedResult, actualResult);
@@ -1104,26 +1104,26 @@
* state was last reset.
*/
Script verifyTimeZoneNotChanged() {
- mFakeCallback.assertTimeZoneNotChanged();
+ mFakeEnvironment.assertTimeZoneNotChanged();
return this;
}
/** Verifies the device's time zone has been set and clears change tracking history. */
Script verifyTimeZoneChangedAndReset(String zoneId) {
- mFakeCallback.assertTimeZoneChangedTo(zoneId);
- mFakeCallback.commitAllChanges();
+ mFakeEnvironment.assertTimeZoneChangedTo(zoneId);
+ mFakeEnvironment.commitAllChanges();
return this;
}
Script verifyTimeZoneChangedAndReset(ManualTimeZoneSuggestion suggestion) {
- mFakeCallback.assertTimeZoneChangedTo(suggestion.getZoneId());
- mFakeCallback.commitAllChanges();
+ mFakeEnvironment.assertTimeZoneChangedTo(suggestion.getZoneId());
+ mFakeEnvironment.commitAllChanges();
return this;
}
Script verifyTimeZoneChangedAndReset(TelephonyTimeZoneSuggestion suggestion) {
- mFakeCallback.assertTimeZoneChangedTo(suggestion.getZoneId());
- mFakeCallback.commitAllChanges();
+ mFakeEnvironment.assertTimeZoneChangedTo(suggestion.getZoneId());
+ mFakeEnvironment.commitAllChanges();
return this;
}
@@ -1131,9 +1131,9 @@
* Verifies that the configuration has been changed to the expected value.
*/
Script verifyConfigurationChangedAndReset(ConfigurationInternal expected) {
- mFakeCallback.mConfigurationInternal.assertHasBeenSet();
- assertEquals(expected, mFakeCallback.mConfigurationInternal.getLatest());
- mFakeCallback.commitAllChanges();
+ mFakeEnvironment.mConfigurationInternal.assertHasBeenSet();
+ assertEquals(expected, mFakeEnvironment.mConfigurationInternal.getLatest());
+ mFakeEnvironment.commitAllChanges();
// Also confirm the listener triggered.
mMockConfigChangeListener.verifyOnChangeCalled();
@@ -1146,7 +1146,7 @@
* {@link TimeZoneConfiguration} have been changed.
*/
Script verifyConfigurationNotChanged() {
- mFakeCallback.mConfigurationInternal.assertHasNotBeenSet();
+ mFakeEnvironment.mConfigurationInternal.assertHasNotBeenSet();
// Also confirm the listener did not trigger.
mMockConfigChangeListener.verifyOnChangeNotCalled();
@@ -1154,7 +1154,7 @@
}
Script resetConfigurationTracking() {
- mFakeCallback.commitAllChanges();
+ mFakeEnvironment.commitAllChanges();
return this;
}
}
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/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 37fb0e9..0afdc58 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -66,6 +66,7 @@
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.InsetsState;
+import android.view.RoundedCorners;
import android.view.WindowInsets.Side;
import android.view.WindowInsets.Type;
import android.view.WindowManager;
@@ -99,6 +100,7 @@
private int mRotation = ROTATION_0;
private boolean mHasDisplayCutout;
private boolean mIsLongEdgeDisplayCutout;
+ private boolean mHasRoundedCorners;
private final Rect mDisplayBounds = new Rect();
@@ -153,6 +155,11 @@
updateDisplayFrames();
}
+ public void addRoundedCorners() {
+ mHasRoundedCorners = true;
+ updateDisplayFrames();
+ }
+
private void updateDisplayFrames() {
mFrames = createDisplayFrames(
mDisplayContent.getInsetsStateController().getRawInsetsState());
@@ -166,9 +173,11 @@
private DisplayFrames createDisplayFrames(InsetsState insetsState) {
final Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation,
mHasDisplayCutout, mIsLongEdgeDisplayCutout);
+ final RoundedCorners roundedCorners = mHasRoundedCorners
+ ? mDisplayContent.calculateRoundedCornersForRotation(mRotation)
+ : RoundedCorners.NO_ROUNDED_CORNERS;
return new DisplayFrames(mDisplayContent.getDisplayId(),
- insetsState,
- info.first, info.second);
+ insetsState, info.first, info.second, roundedCorners);
}
@Test
@@ -753,6 +762,8 @@
assertSimulateLayoutSameDisplayFrames();
addDisplayCutout();
assertSimulateLayoutSameDisplayFrames();
+ addRoundedCorners();
+ assertSimulateLayoutSameDisplayFrames();
}
private void assertSimulateLayoutSameDisplayFrames() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 499507e..2163661 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -302,7 +302,7 @@
final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
mImeWindow.mAboveInsetsState = state;
mDisplayContent.mDisplayFrames = new DisplayFrames(mDisplayContent.getDisplayId(),
- state, displayInfo, null /* displayCutout */);
+ state, displayInfo, null /* displayCutout */, null /* roundedCorners*/);
mDisplayContent.setInputMethodWindowLocked(mImeWindow);
mImeWindow.mAttrs.setFitInsetsSides(Side.all() & ~Side.BOTTOM);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 2107ab1e..ee293fc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -86,6 +86,7 @@
assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR));
assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_NAVIGATION_BAR));
+ assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_IME));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index a045100..fa3e3ae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -29,6 +29,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
@@ -51,11 +52,13 @@
SurfaceControl.Transaction mTransaction;
private boolean mAreCornersRounded = false;
+ private int mColor = Color.BLACK;
@Before
public void setUp() throws Exception {
mSurfaces = new SurfaceControlMocker();
- mLetterbox = new Letterbox(mSurfaces, StubTransaction::new, () -> mAreCornersRounded);
+ mLetterbox = new Letterbox(mSurfaces, StubTransaction::new,
+ () -> mAreCornersRounded, () -> Color.valueOf(mColor));
mTransaction = spy(StubTransaction.class);
}
@@ -171,6 +174,18 @@
}
@Test
+ public void testApplySurfaceChanges_setColor() {
+ mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
+ mLetterbox.applySurfaceChanges(mTransaction);
+
+ verify(mTransaction).setColor(mSurfaces.top, new float[]{0, 0, 0});
+
+ mColor = Color.GREEN;
+
+ assertTrue(mLetterbox.needsApplySurfaceChanges());
+ }
+
+ @Test
public void testApplySurfaceChanges_cornersNotRounded_surfaceBehindNotCreated() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
mLetterbox.applySurfaceChanges(mTransaction);
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..6f775cf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -39,6 +39,8 @@
import static com.android.server.wm.Task.ActivityState.STOPPED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -52,14 +54,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 +72,6 @@
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
-import java.util.ArrayList;
-
/**
* Tests for Size Compatibility mode.
*
@@ -121,13 +119,13 @@
@Test
public void testKeepBoundsWhenChangingFromFreeformToFullscreen() {
removeGlobalMinSizeRestriction();
- // create freeform display and a freeform app
+ // Create landscape freeform display and a freeform app.
DisplayContent display = new TestDisplayContent.Builder(mAtm, 2000, 1000)
.setCanRotate(false)
.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM).build();
setUpApp(display);
- // Put app window into freeform and then make it a compat app.
+ // Put app window into portrait freeform and then make it a compat app.
final Rect bounds = new Rect(100, 100, 400, 600);
mTask.setBounds(bounds);
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
@@ -140,7 +138,7 @@
final int density = mActivity.getConfiguration().densityDpi;
- // change display configuration to fullscreen
+ // Change display configuration to fullscreen.
Configuration c = new Configuration(display.getRequestedOverrideConfiguration());
c.windowConfiguration.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
display.onRequestedOverrideConfigurationChanged(c);
@@ -150,6 +148,8 @@
assertEquals(bounds.width(), mActivity.getBounds().width());
assertEquals(bounds.height(), mActivity.getBounds().height());
assertEquals(density, mActivity.getConfiguration().densityDpi);
+ // Size compat mode is sandboxed at the activity level.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -175,6 +175,12 @@
assertEquals(appBounds.height(), appBounds.width() * aspectRatio, 0.5f /* delta */);
// The decor height should be a part of the effective bounds.
assertEquals(mActivity.getBounds().height(), appBounds.height() + notchHeight);
+ // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
+ assertActivityMaxBoundsSandboxedForLetterbox();
+ // Activity max bounds ignore notch, since an app can be shown past the notch (although app
+ // is currently limited by the notch).
+ assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
+ .isEqualTo(displayBounds.height());
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
assertFitted();
@@ -184,9 +190,17 @@
assertEquals(appBounds.width(), appBounds.height() * aspectRatio, 0.5f /* delta */);
// The notch is no longer on top.
assertEquals(appBounds, mActivity.getBounds());
+ // Activity max bounds are sandboxed.
+ assertActivityMaxBoundsSandboxedForLetterbox();
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
+ // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
+ assertActivityMaxBoundsSandboxedForLetterbox();
+ // Activity max bounds ignore notch, since an app can be shown past the notch (although app
+ // is currently limited by the notch).
+ assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
+ .isEqualTo(displayBounds.height());
}
@Test
@@ -214,6 +228,9 @@
assertEquals(originalBounds.width(), mActivity.getBounds().width());
assertEquals(originalBounds.height(), mActivity.getBounds().height());
assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
+ // Activity is sandboxed; it is in size compat mode since it is not resizable and has a
+ // max aspect ratio.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
assertScaled();
}
@@ -221,11 +238,13 @@
public void testFixedScreenBoundsWhenDisplaySizeChanged() {
setUpDisplaySizeWithApp(1000, 2500);
prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+ final DisplayContent display = mActivity.mDisplayContent;
assertFitted();
+ // Activity and task inherit bounds from TaskDisplayArea, since not sandboxed.
+ assertMaxBoundsInheritDisplayAreaBounds();
final Rect origBounds = new Rect(mActivity.getBounds());
final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
- final DisplayContent display = mActivity.mDisplayContent;
// Change the size of current display.
resizeDisplay(display, 1000, 2000);
@@ -242,6 +261,8 @@
// The position of configuration bounds should be the same as compat bounds.
assertEquals(mActivity.getBounds().left, currentBounds.left);
assertEquals(mActivity.getBounds().top, currentBounds.top);
+ // Activity is sandboxed to the offset size compat bounds.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
// Change display size to a different orientation
resizeDisplay(display, 2000, 1000);
@@ -250,6 +271,8 @@
assertEquals(origBounds.height(), currentBounds.height());
assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation);
+ // Activity is sandboxed to the offset size compat bounds.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
// The previous resize operation doesn't consider the rotation change after size changed.
// These setups apply the requested orientation to rotation as real case that the top fixed
@@ -269,6 +292,8 @@
assertEquals(origBounds.height(), currentBounds.height());
assertEquals(offsetX, currentBounds.left);
assertScaled();
+ // Activity is sandboxed due to size compat mode.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -284,6 +309,8 @@
assertEquals(bounds.width(), bounds.height() * maxAspect, 0.0001f /* delta */);
// The position should be horizontal centered.
assertEquals((displayWidth - bounds.width()) / 2, bounds.left);
+ // Activity max bounds should be sandboxed since it is letterboxed.
+ assertActivityMaxBoundsSandboxedForLetterbox();
mActivity.mDisplayContent.setImeLayeringTarget(addWindowToActivity(mActivity));
// Make sure IME cannot attach to the app, otherwise IME window will also be shifted.
@@ -295,6 +322,8 @@
// It should keep non-attachable because the resolved bounds will be computed according to
// the aspect ratio that won't match its parent bounds.
assertFalse(mActivity.mDisplayContent.isImeAttachedToApp());
+ // Activity max bounds should be sandboxed since it is letterboxed.
+ assertActivityMaxBoundsSandboxedForLetterbox();
}
@Test
@@ -320,14 +349,13 @@
}
@Test
- public void testMoveToDifferentOrientDisplay() {
+ public void testMoveToDifferentOrientationDisplay() {
setUpDisplaySizeWithApp(1000, 2500);
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
- final Rect configBounds = mActivity.getWindowConfiguration().getBounds();
- final int origWidth = configBounds.width();
- final int origHeight = configBounds.height();
+ final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
+ final Rect originalBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
final int notchHeight = 100;
final DisplayContent newDisplay = new TestDisplayContent.Builder(mAtm, 2000, 1000)
@@ -336,37 +364,44 @@
// Move the non-resizable activity to the new display.
mTask.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
// The configuration bounds [820, 0 - 1820, 2500] should keep the same.
- assertEquals(origWidth, configBounds.width());
- assertEquals(origHeight, configBounds.height());
+ assertEquals(originalBounds.width(), currentBounds.width());
+ assertEquals(originalBounds.height(), currentBounds.height());
assertScaled();
+ // Activity max bounds are sandboxed due to size compat mode on the new display.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
final Rect newDisplayBounds = newDisplay.getWindowConfiguration().getBounds();
// The scaled bounds should exclude notch area (1000 - 100 == 360 * 2500 / 1000 = 900).
assertEquals(newDisplayBounds.height() - notchHeight,
- (int) ((float) mActivity.getBounds().width() * origHeight / origWidth));
+ (int) ((float) mActivity.getBounds().width() * originalBounds.height()
+ / originalBounds.width()));
// Recompute the natural configuration in the new display.
mActivity.clearSizeCompatMode();
mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
// Because the display cannot rotate, the portrait activity will fit the short side of
// display with keeping portrait bounds [200, 0 - 700, 1000] in center.
- assertEquals(newDisplayBounds.height(), configBounds.height());
- assertEquals(configBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
- configBounds.width());
+ assertEquals(newDisplayBounds.height(), currentBounds.height());
+ assertEquals(currentBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
+ currentBounds.width());
assertFitted();
// The appBounds should be [200, 100 - 700, 1000].
final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
- assertEquals(configBounds.width(), appBounds.width());
- assertEquals(configBounds.height() - notchHeight, appBounds.height());
+ assertEquals(currentBounds.width(), appBounds.width());
+ assertEquals(currentBounds.height() - notchHeight, appBounds.height());
+ // Task max bounds are sandboxed due to letterboxing from orientation mismatch with display.
+ assertTaskMaxBoundsSandboxed();
}
@Test
- public void testFixedOrientRotateCutoutDisplay() {
+ public void testFixedOrientationRotateCutoutDisplay() {
// Create a display with a notch/cutout
final int notchHeight = 60;
- setUpApp(new TestDisplayContent.Builder(mAtm, 1000, 2500)
+ final int width = 1000;
+ setUpApp(new TestDisplayContent.Builder(mAtm, width, 2500)
.setNotch(notchHeight).build());
- // Bounds=[0, 0 - 1000, 1460], AppBounds=[0, 60 - 1000, 1460].
+ // Bounds=[0, 0 - 1000, 1400], AppBounds=[0, 60 - 1000, 1460].
+ final float maxAspect = 1.4f;
prepareUnresizable(mActivity, 1.4f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
@@ -374,6 +409,11 @@
final Rect origBounds = new Rect(currentBounds);
final Rect origAppBounds = new Rect(appBounds);
+ // Activity is sandboxed, and bounds include the area consumed by the notch.
+ assertActivityMaxBoundsSandboxedForLetterbox();
+ assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds().height())
+ .isEqualTo(Math.round(width * maxAspect) + notchHeight);
+
// Although the activity is fixed orientation, force rotate the display.
rotateDisplay(mActivity.mDisplayContent, ROTATION_270);
assertEquals(ROTATION_270, mTask.getWindowConfiguration().getRotation());
@@ -389,10 +429,13 @@
// The position in configuration should be global coordinates.
assertEquals(mActivity.getBounds().left, currentBounds.left);
assertEquals(mActivity.getBounds().top, currentBounds.top);
+
+ // Activity max bounds are sandboxed due to size compat mode.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
- public void testFixedAspOrientChangeOrient() {
+ public void testFixedAspectRatioOrientationChangeOrientation() {
setUpDisplaySizeWithApp(1000, 2500);
final float maxAspect = 1.4f;
@@ -404,6 +447,8 @@
final Rect originalAppBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds());
assertEquals((int) (originalBounds.width() * maxAspect), originalBounds.height());
+ // Activity is sandboxed due to fixed aspect ratio.
+ assertActivityMaxBoundsSandboxedForLetterbox();
// Change the fixed orientation.
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
@@ -415,6 +460,8 @@
mActivity.getWindowConfiguration().getAppBounds().height());
assertEquals(originalAppBounds.height(),
mActivity.getWindowConfiguration().getAppBounds().width());
+ // Activity is sandboxed due to fixed aspect ratio.
+ assertActivityMaxBoundsSandboxedForLetterbox();
}
@Test
@@ -463,6 +510,8 @@
// restarted and the override configuration won't be cleared.
verify(mActivity, never()).restartProcessIfVisible();
assertScaled();
+ // Activity max bounds are sandboxed due to size compat mode, even if is not visible.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
// Change display density
display.mBaseDisplayDensity = (int) (0.7f * display.mBaseDisplayDensity);
@@ -479,52 +528,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.
*/
@@ -583,12 +586,16 @@
// in multi-window mode.
mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
assertFalse(activity.shouldUseSizeCompatMode());
+ // Activity and task should not be sandboxed.
+ assertMaxBoundsInheritDisplayAreaBounds();
// The non-resizable activity should not be size compat because the display support
// changing windowing mode from fullscreen to freeform.
mTask.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
assertFalse(activity.shouldUseSizeCompatMode());
+ // Activity and task should not be sandboxed.
+ assertMaxBoundsInheritDisplayAreaBounds();
}
@Test
@@ -652,6 +659,9 @@
// be transparent.
assertFalse(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
+ // Activity is sandboxed.
+ assertActivityMaxBoundsSandboxedForLetterbox();
+
// Make the activity fill the display.
prepareUnresizable(mActivity, 10 /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE);
w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
@@ -661,6 +671,7 @@
// The letterbox should only cover the notch area, so status bar can be transparent.
assertEquals(new Rect(notchHeight, 0, 0, 0), mActivity.getLetterboxInsets());
assertTrue(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
+ assertActivityMaxBoundsSandboxedForLetterbox();
}
@Test
@@ -685,6 +696,8 @@
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
assertEquals(taskBounds, activityBounds);
+ // Activity inherits max bounds from task, since sandboxing applied to task.
+ assertTaskMaxBoundsSandboxed();
// Task bounds should be 700x1400 with the ratio as the display.
assertEquals(displayBounds.height(), taskBounds.height());
@@ -715,6 +728,8 @@
assertScaled();
assertEquals(activityBounds.width(), newActivityBounds.width());
assertEquals(activityBounds.height(), newActivityBounds.height());
+ // Activity max bounds are sandboxed due to size compat mode.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -726,29 +741,30 @@
// Portrait fixed app without max aspect.
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
- Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
- Rect activityBounds = new Rect(mActivity.getBounds());
-
// App should launch in fullscreen.
assertFalse(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
- assertEquals(displayBounds, activityBounds);
+ // Activity and task inherit max bounds from TaskDisplayArea.
+ assertMaxBoundsInheritDisplayAreaBounds();
// Rotate display to landscape.
rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
- displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
- activityBounds = new Rect(mActivity.getBounds());
- assertTrue(displayBounds.width() > displayBounds.height());
+ final Rect rotatedDisplayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+ final Rect rotatedActivityBounds = new Rect(mActivity.getBounds());
+ assertTrue(rotatedDisplayBounds.width() > rotatedDisplayBounds.height());
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
+ assertThat(mActivity.inSizeCompatMode()).isTrue();
+ // Activity max bounds are sandboxed due to size compat mode.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
// App bounds should be 700x1400 with the ratio as the display.
- assertEquals(displayBounds.height(), activityBounds.height());
- assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
- activityBounds.width());
+ assertEquals(rotatedDisplayBounds.height(), rotatedActivityBounds.height());
+ assertEquals(rotatedDisplayBounds.height() * rotatedDisplayBounds.height()
+ / rotatedDisplayBounds.width(), rotatedActivityBounds.width());
}
@Test
@@ -781,14 +797,17 @@
final Rect displayBounds = new Rect(display.getBounds());
final Rect taskBounds = new Rect(mTask.getBounds());
final Rect newActivityBounds = new Rect(newActivity.getBounds());
+ final float displayAspectRatio = (float) displayBounds.height() / displayBounds.width();
// Task and app bounds should be 700x1400 with the ratio as the display.
assertTrue(mTask.isTaskLetterboxed());
assertFalse(newActivity.inSizeCompatMode());
assertEquals(taskBounds, newActivityBounds);
assertEquals(displayBounds.height(), taskBounds.height());
- assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
- taskBounds.width());
+ assertThat(taskBounds.width())
+ .isEqualTo(Math.round(displayBounds.height() * displayAspectRatio));
+ // Task max bounds are sandboxed due to letterbox, with the ratio of the display.
+ assertTaskMaxBoundsSandboxed();
}
@Test
@@ -828,6 +847,14 @@
assertEquals(displayBounds.height(), taskBounds.height());
assertEquals((long) Math.rint(taskBounds.height() / newActivity.info.maxAspectRatio),
taskBounds.width());
+ // New activity max bounds are sandboxed due to letterbox.
+ assertThat(newActivity.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(taskBounds);
+ // Task max bounds are sandboxed due to letterbox, with the ratio of the display.
+ assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds().height())
+ .isEqualTo(displayBounds.height());
+ assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds().width())
+ .isEqualTo(Math.round(displayBounds.height() / newActivity.info.maxAspectRatio));
// App bounds should be fullscreen in Task bounds.
assertFalse(newActivity.inSizeCompatMode());
@@ -856,6 +883,9 @@
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
+ assertThat(mActivity.inSizeCompatMode()).isTrue();
+ // Activity max bounds are sandboxed due to size compat mode.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
final Rect activityBounds = new Rect(mActivity.getBounds());
mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
@@ -866,6 +896,8 @@
assertScaled();
assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
assertEquals(activityBounds, mActivity.getBounds());
+ // Activity max bounds are sandboxed due to size compat.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -881,6 +913,7 @@
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
+ assertTaskMaxBoundsSandboxed();
// Rotate display to portrait.
rotateDisplay(display, ROTATION_90);
@@ -888,6 +921,7 @@
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
+ assertActivityMaxBoundsSandboxedForSizeCompat();
// Rotate display to landscape.
rotateDisplay(display, ROTATION_180);
@@ -895,6 +929,7 @@
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
+ assertTaskMaxBoundsSandboxed();
}
@Test
@@ -912,20 +947,26 @@
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
+ // Task is letterboxed due to mismatched orientation request.
+ assertTaskMaxBoundsSandboxed();
- // Rotate display to portrait.
+ // Rotate display to landscape.
rotateDisplay(display, ROTATION_90);
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
+ // Activity max bounds are sandboxed due to unresizable app.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
- // Rotate display to landscape.
+ // Rotate display to portrait.
rotateDisplay(display, ROTATION_180);
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
+ // Task is letterboxed, as in first case.
+ assertTaskMaxBoundsSandboxed();
}
@Test
@@ -942,12 +983,18 @@
assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
assertEquals(2800, displayBounds.width());
assertEquals(1400, displayBounds.height());
- taskDisplayArea.setBounds(0, 0, 2400, 1000);
+ Rect displayAreaBounds = new Rect(0, 0, 2400, 1000);
+ taskDisplayArea.setBounds(displayAreaBounds);
final Rect activityBounds = new Rect(mActivity.getBounds());
assertFalse(mActivity.inSizeCompatMode());
assertEquals(2400, activityBounds.width());
assertEquals(1000, activityBounds.height());
+ // Task and activity maximum bounds inherit from TaskDisplayArea bounds.
+ assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(displayAreaBounds);
+ assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(displayAreaBounds);
}
@Test
@@ -1095,6 +1142,48 @@
assertFalse(mActivity.hasSizeCompatBounds());
}
+ /** Asserts both the activity and task max bounds inherit from the TaskDisplayArea. */
+ private void assertMaxBoundsInheritDisplayAreaBounds() {
+ final Rect taskDisplayAreaBounds = mTask.getDisplayArea().getBounds();
+ assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(taskDisplayAreaBounds);
+ assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(taskDisplayAreaBounds);
+ }
+
+ /**
+ * Asserts task-level letterboxing, so both activity and task max bounds
+ * are sandboxed to the letterbox bounds.
+ */
+ private void assertTaskMaxBoundsSandboxed() {
+ // Activity inherits max bounds from task, since sandboxing applied to task.
+ assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(mTask.getBounds());
+ // Task max bounds are sandboxed due to letterbox.
+ assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(mTask.getBounds());
+ }
+
+ /** Asserts activity-level size compat mode, so only activity max bounds are sandboxed. */
+ private void assertActivityMaxBoundsSandboxedForSizeCompat() {
+ // Activity max bounds are sandboxed due to size compat mode.
+ assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(mActivity.getWindowConfiguration().getBounds());
+ // Task inherits max bounds from display.
+ assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(mTask.getDisplayContent().getBounds());
+ }
+
+ /** Asserts activity-level letterboxing, so only activity max bounds are sandboxed. */
+ private void assertActivityMaxBoundsSandboxedForLetterbox() {
+ // Activity is sandboxed due to fixed aspect ratio.
+ assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(mActivity.getBounds());
+ // Task inherits bounds from display.
+ assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(mTask.getDisplayContent().getBounds());
+ }
+
static Configuration rotateDisplay(DisplayContent display, int rotation) {
final Configuration c = new Configuration();
display.getDisplayRotation().setRotation(rotation);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index ae85ceb..d71993d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -136,6 +136,11 @@
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
mInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
final TestDisplayContent newDisplay = createInternal(display);
+ // Ensure letterbox aspect ratio is not overridden on any device target.
+ // {@link com.android.internal.R.dimen.config_taskLetterboxAspectRatio}, provided by
+ // the below method, is set on some device form factors.
+ mService.mWindowManager.setTaskLetterboxAspectRatio(0);
+
// disable the normal system decorations
final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy();
spyOn(displayPolicy);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index 53cea5a..d1d0ac6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -47,6 +47,7 @@
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.InsetsState;
+import android.view.RoundedCorners;
import android.view.Surface;
import android.view.WindowManager;
@@ -146,7 +147,7 @@
final DisplayInfo info = dc.computeScreenConfiguration(config, Surface.ROTATION_0);
final WmDisplayCutout cutout = dc.calculateDisplayCutoutForRotation(Surface.ROTATION_0);
final DisplayFrames displayFrames = new DisplayFrames(dc.getDisplayId(), new InsetsState(),
- info, cutout);
+ info, cutout, RoundedCorners.NO_ROUNDED_CORNERS);
wallpaperWindowToken.applyFixedRotationTransform(info, displayFrames, config);
// Check that the wallpaper has the same frame in landscape than in portrait
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/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index c945aea..0792e8b 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -18,28 +18,7 @@
name: "FlickerTests",
srcs: ["src/**/*.java", "src/**/*.kt"],
manifest: "AndroidManifest.xml",
- test_config: "AndroidTestPhysicalDevices.xml",
- platform_apis: true,
- certificate: "platform",
- test_suites: ["device-tests"],
- libs: ["android.test.runner"],
- static_libs: [
- "androidx.test.ext.junit",
- "flickertestapplib",
- "flickerlib",
- "truth-prebuilt",
- "launcher-helper-lib",
- "launcher-aosp-tapl",
- "platform-test-annotations",
- ],
-}
-
-
-android_test {
- name: "FlickerTestsVirtual",
- srcs: ["src/**/*.java", "src/**/*.kt"],
- manifest: "AndroidManifest.xml",
- test_config: "AndroidTestVirtualDevices.xml",
+ test_config: "AndroidTest.xml",
platform_apis: true,
certificate: "platform",
test_suites: ["device-tests"],
diff --git a/tests/FlickerTests/AndroidTestPhysicalDevices.xml b/tests/FlickerTests/AndroidTest.xml
similarity index 95%
rename from tests/FlickerTests/AndroidTestPhysicalDevices.xml
rename to tests/FlickerTests/AndroidTest.xml
index b1cee5c..e68fbd8 100644
--- a/tests/FlickerTests/AndroidTestPhysicalDevices.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -25,7 +25,6 @@
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="com.android.server.wm.flicker"/>
- <option name="include-annotation" value="androidx.test.filters.RequiresDevice" />
<option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
<option name="shell-timeout" value="6600s" />
<option name="test-timeout" value="6600s" />
diff --git a/tests/FlickerTests/AndroidTestVirtualDevices.xml b/tests/FlickerTests/AndroidTestVirtualDevices.xml
deleted file mode 100644
index 9a5413a..0000000
--- a/tests/FlickerTests/AndroidTestVirtualDevices.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright 2018 Google Inc. All Rights Reserved.
- -->
-<configuration description="Runs WindowManager Flicker Tests">
- <option name="test-tag" value="FlickerTests" />
- <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
- <!-- keeps the screen on during tests -->
- <option name="screen-always-on" value="on" />
- <!-- prevents the phone from restarting -->
- <option name="force-skip-system-props" value="true" />
- <!-- set WM tracing verbose level to all -->
- <option name="run-command" value="cmd window tracing level all" />
- <!-- inform WM to log all transactions -->
- <option name="run-command" value="cmd window tracing transaction" />
- <!-- restart launcher to activate TAPL -->
- <option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" />
- </target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.DeviceCleaner">
- <!-- reboot the device to teardown any crashed tests -->
- <option name="cleanup-action" value="REBOOT" />
- </target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="cleanup-apks" value="true"/>
- <option name="test-file-name" value="FlickerTests.apk"/>
- <option name="test-file-name" value="FlickerTestApp.apk" />
- </target_preparer>
- <test class="com.android.tradefed.testtype.AndroidJUnitTest">
- <option name="package" value="com.android.server.wm.flicker"/>
- <option name="exclude-annotation" value="androidx.test.filters.RequiresDevice" />
- <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
- <option name="shell-timeout" value="6600s" />
- <option name="test-timeout" value="6000s" />
- <option name="hidden-api-checks" value="false" />
- </test>
- <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
- <option name="directory-keys" value="/sdcard/flicker" />
- <option name="collect-on-run-ended-only" value="true" />
- <option name="clean-up" value="true" />
- </metrics_collector>
-</configuration>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 6b6d21b..89c6663 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -17,9 +17,9 @@
package com.android.server.wm.flicker
import android.platform.helpers.IAppHelper
-import com.android.server.wm.flicker.dsl.EventLogAssertionBuilder
-import com.android.server.wm.flicker.dsl.LayersAssertionBuilder
-import com.android.server.wm.flicker.dsl.WmAssertionBuilder
+import com.android.server.wm.flicker.dsl.EventLogAssertionBuilderLegacy
+import com.android.server.wm.flicker.dsl.LayersAssertionBuilderLegacy
+import com.android.server.wm.flicker.dsl.WmAssertionBuilderLegacy
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_LAYER_NAME
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_WINDOW_NAME
@@ -31,7 +31,7 @@
const val WALLPAPER_TITLE = "Wallpaper"
@JvmOverloads
-fun WmAssertionBuilder.statusBarWindowIsAlwaysVisible(
+fun WmAssertionBuilderLegacy.statusBarWindowIsAlwaysVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -41,7 +41,7 @@
}
@JvmOverloads
-fun WmAssertionBuilder.navBarWindowIsAlwaysVisible(
+fun WmAssertionBuilderLegacy.navBarWindowIsAlwaysVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -50,7 +50,7 @@
}
}
-fun WmAssertionBuilder.visibleWindowsShownMoreThanOneConsecutiveEntry(
+fun WmAssertionBuilderLegacy.visibleWindowsShownMoreThanOneConsecutiveEntry(
ignoreWindows: List<String> = emptyList(),
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -60,7 +60,7 @@
}
}
-fun WmAssertionBuilder.launcherReplacesAppWindowAsTopWindow(
+fun WmAssertionBuilderLegacy.launcherReplacesAppWindowAsTopWindow(
testApp: IAppHelper,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -72,7 +72,7 @@
}
}
-fun WmAssertionBuilder.wallpaperWindowBecomesVisible(
+fun WmAssertionBuilderLegacy.wallpaperWindowBecomesVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -83,7 +83,7 @@
}
}
-fun WmAssertionBuilder.wallpaperWindowBecomesInvisible(
+fun WmAssertionBuilderLegacy.wallpaperWindowBecomesInvisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -94,7 +94,7 @@
}
}
-fun WmAssertionBuilder.appWindowAlwaysVisibleOnTop(
+fun WmAssertionBuilderLegacy.appWindowAlwaysVisibleOnTop(
packageName: String,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -104,7 +104,7 @@
}
}
-fun WmAssertionBuilder.appWindowBecomesVisible(
+fun WmAssertionBuilderLegacy.appWindowBecomesVisible(
appName: String,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -116,7 +116,7 @@
}
}
-fun WmAssertionBuilder.appWindowBecomesInVisible(
+fun WmAssertionBuilderLegacy.appWindowBecomesInVisible(
appName: String,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -129,7 +129,7 @@
}
@JvmOverloads
-fun LayersAssertionBuilder.noUncoveredRegions(
+fun LayersAssertionBuilderLegacy.noUncoveredRegions(
beginRotation: Int,
endRotation: Int = beginRotation,
allStates: Boolean = true,
@@ -159,7 +159,7 @@
}
@JvmOverloads
-fun LayersAssertionBuilder.navBarLayerIsAlwaysVisible(
+fun LayersAssertionBuilderLegacy.navBarLayerIsAlwaysVisible(
rotatesScreen: Boolean = false,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -180,7 +180,7 @@
}
@JvmOverloads
-fun LayersAssertionBuilder.statusBarLayerIsAlwaysVisible(
+fun LayersAssertionBuilderLegacy.statusBarLayerIsAlwaysVisible(
rotatesScreen: Boolean = false,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -201,7 +201,7 @@
}
@JvmOverloads
-fun LayersAssertionBuilder.navBarLayerRotatesAndScales(
+fun LayersAssertionBuilderLegacy.navBarLayerRotatesAndScales(
beginRotation: Int,
endRotation: Int = beginRotation,
bugId: Int = 0,
@@ -225,7 +225,7 @@
}
@JvmOverloads
-fun LayersAssertionBuilder.statusBarLayerRotatesScales(
+fun LayersAssertionBuilderLegacy.statusBarLayerRotatesScales(
beginRotation: Int,
endRotation: Int = beginRotation,
bugId: Int = 0,
@@ -242,7 +242,7 @@
}
}
-fun LayersAssertionBuilder.visibleLayersShownMoreThanOneConsecutiveEntry(
+fun LayersAssertionBuilderLegacy.visibleLayersShownMoreThanOneConsecutiveEntry(
ignoreLayers: List<String> = emptyList(),
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -252,7 +252,7 @@
}
}
-fun LayersAssertionBuilder.appLayerReplacesWallpaperLayer(
+fun LayersAssertionBuilderLegacy.appLayerReplacesWallpaperLayer(
appName: String,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -264,7 +264,7 @@
}
}
-fun LayersAssertionBuilder.wallpaperLayerReplacesAppLayer(
+fun LayersAssertionBuilderLegacy.wallpaperLayerReplacesAppLayer(
testApp: IAppHelper,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -276,7 +276,7 @@
}
}
-fun LayersAssertionBuilder.layerAlwaysVisible(
+fun LayersAssertionBuilderLegacy.layerAlwaysVisible(
packageName: String,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -286,7 +286,7 @@
}
}
-fun LayersAssertionBuilder.layerBecomesVisible(
+fun LayersAssertionBuilderLegacy.layerBecomesVisible(
packageName: String,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -298,7 +298,7 @@
}
}
-fun LayersAssertionBuilder.layerBecomesInvisible(
+fun LayersAssertionBuilderLegacy.layerBecomesInvisible(
packageName: String,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -310,7 +310,7 @@
}
}
-fun EventLogAssertionBuilder.focusChanges(
+fun EventLogAssertionBuilderLegacy.focusChanges(
vararg windows: String,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -320,7 +320,7 @@
}
}
-fun EventLogAssertionBuilder.focusDoesNotChange(
+fun EventLogAssertionBuilderLegacy.focusDoesNotChange(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
index 96c2009..c775cb8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
@@ -17,13 +17,13 @@
package com.android.server.wm.flicker.ime
import android.platform.helpers.IAppHelper
-import com.android.server.wm.flicker.dsl.LayersAssertionBuilder
-import com.android.server.wm.flicker.dsl.WmAssertionBuilder
+import com.android.server.wm.flicker.dsl.LayersAssertionBuilderLegacy
+import com.android.server.wm.flicker.dsl.WmAssertionBuilderLegacy
const val IME_WINDOW_TITLE = "InputMethod"
@JvmOverloads
-fun LayersAssertionBuilder.imeLayerBecomesVisible(
+fun LayersAssertionBuilderLegacy.imeLayerBecomesVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -34,7 +34,7 @@
}
}
-fun LayersAssertionBuilder.imeLayerBecomesInvisible(
+fun LayersAssertionBuilderLegacy.imeLayerBecomesInvisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -45,7 +45,7 @@
}
}
-fun LayersAssertionBuilder.imeAppLayerIsAlwaysVisible(
+fun LayersAssertionBuilderLegacy.imeAppLayerIsAlwaysVisible(
testApp: IAppHelper,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -55,7 +55,7 @@
}
}
-fun WmAssertionBuilder.imeAppWindowIsAlwaysVisible(
+fun WmAssertionBuilderLegacy.imeAppWindowIsAlwaysVisible(
testApp: IAppHelper,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -65,7 +65,7 @@
}
}
-fun WmAssertionBuilder.imeWindowBecomesVisible(
+fun WmAssertionBuilderLegacy.imeWindowBecomesVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -76,7 +76,7 @@
}
}
-fun WmAssertionBuilder.imeWindowBecomesInvisible(
+fun WmAssertionBuilderLegacy.imeWindowBecomesInvisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -87,7 +87,7 @@
}
}
-fun WmAssertionBuilder.imeAppWindowBecomesVisible(
+fun WmAssertionBuilderLegacy.imeAppWindowBecomesVisible(
windowName: String,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -99,7 +99,7 @@
}
}
-fun WmAssertionBuilder.imeAppWindowBecomesInvisible(
+fun WmAssertionBuilderLegacy.imeAppWindowBecomesInvisible(
testApp: IAppHelper,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -111,7 +111,7 @@
}
}
-fun LayersAssertionBuilder.imeAppLayerBecomesInvisible(
+fun LayersAssertionBuilderLegacy.imeAppLayerBecomesInvisible(
testApp: IAppHelper,
bugId: Int = 0,
enabled: Boolean = bugId == 0
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
index ba2ee5f..1bd1190 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
@@ -17,9 +17,9 @@
package com.android.server.wm.flicker.launch
import android.platform.helpers.IAppHelper
-import com.android.server.wm.flicker.dsl.WmAssertionBuilder
+import com.android.server.wm.flicker.dsl.WmAssertionBuilderLegacy
-fun WmAssertionBuilder.appWindowReplacesLauncherAsTopWindow(
+fun WmAssertionBuilderLegacy.appWindowReplacesLauncherAsTopWindow(
testApp: IAppHelper,
bugId: Int = 0,
enabled: Boolean = bugId == 0
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/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index bf73134..eac8c292 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -132,6 +132,7 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.AppOpsManager;
import android.app.NotificationManager;
@@ -252,6 +253,7 @@
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.net.module.util.ArrayTrackRecord;
import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
import com.android.server.connectivity.ConnectivityConstants;
import com.android.server.connectivity.MockableSystemProperties;
@@ -906,28 +908,69 @@
}
/**
- * A NetworkFactory that allows tests to wait until any in-flight NetworkRequest add or remove
- * operations have been processed. Before ConnectivityService can add or remove any requests,
- * the factory must be told to expect those operations by calling expectAddRequestsWithScores or
- * expectRemoveRequests.
+ * A NetworkFactory that allows to wait until any in-flight NetworkRequest add or remove
+ * operations have been processed and test for them.
*/
private static class MockNetworkFactory extends NetworkFactory {
private final ConditionVariable mNetworkStartedCV = new ConditionVariable();
private final ConditionVariable mNetworkStoppedCV = new ConditionVariable();
private final AtomicBoolean mNetworkStarted = new AtomicBoolean(false);
- // Used to expect that requests be removed or added on a separate thread, without sleeping.
- // Callers can call either expectAddRequestsWithScores() or expectRemoveRequests() exactly
- // once, then cause some other thread to add or remove requests, then call
- // waitForRequests().
- // It is not possible to wait for both add and remove requests. When adding, the queue
- // contains the expected score. When removing, the value is unused, all matters is the
- // number of objects in the queue.
- private final LinkedBlockingQueue<Integer> mExpectations;
+ static class RequestEntry {
+ @NonNull
+ public final NetworkRequest request;
- // Whether we are currently expecting requests to be added or removed. Valid only if
- // mExpectations is non-empty.
- private boolean mExpectingAdditions;
+ RequestEntry(@NonNull final NetworkRequest request) {
+ this.request = request;
+ }
+
+ static final class Add extends RequestEntry {
+ public final int factorySerialNumber;
+
+ Add(@NonNull final NetworkRequest request, final int factorySerialNumber) {
+ super(request);
+ this.factorySerialNumber = factorySerialNumber;
+ }
+ }
+
+ static final class Remove extends RequestEntry {
+ Remove(@NonNull final NetworkRequest request) {
+ super(request);
+ }
+ }
+ }
+
+ // History of received requests adds and removes.
+ private final ArrayTrackRecord<RequestEntry>.ReadHead mRequestHistory =
+ new ArrayTrackRecord<RequestEntry>().newReadHead();
+
+ private static <T> T failIfNull(@Nullable final T obj, @Nullable final String message) {
+ if (null == obj) fail(null != message ? message : "Must not be null");
+ return obj;
+ }
+
+
+ public RequestEntry.Add expectRequestAdd() {
+ return failIfNull((RequestEntry.Add) mRequestHistory.poll(TIMEOUT_MS,
+ it -> it instanceof RequestEntry.Add), "Expected request add");
+ }
+
+ public void expectRequestAdds(final int count) {
+ for (int i = count; i > 0; --i) {
+ expectRequestAdd();
+ }
+ }
+
+ public RequestEntry.Remove expectRequestRemove() {
+ return failIfNull((RequestEntry.Remove) mRequestHistory.poll(TIMEOUT_MS,
+ it -> it instanceof RequestEntry.Remove), "Expected request remove");
+ }
+
+ public void expectRequestRemoves(final int count) {
+ for (int i = count; i > 0; --i) {
+ expectRequestRemove();
+ }
+ }
// Used to collect the networks requests managed by this factory. This is a duplicate of
// the internal information stored in the NetworkFactory (which is private).
@@ -936,7 +979,6 @@
public MockNetworkFactory(Looper looper, Context context, String logTag,
NetworkCapabilities filter) {
super(looper, context, logTag, filter);
- mExpectations = new LinkedBlockingQueue<>();
}
public int getMyRequestCount() {
@@ -970,95 +1012,33 @@
@Override
protected void handleAddRequest(NetworkRequest request, int score,
int factorySerialNumber) {
- synchronized (mExpectations) {
- final Integer expectedScore = mExpectations.poll(); // null if the queue is empty
-
- assertNotNull("Added more requests than expected (" + request + " score : "
- + score + ")", expectedScore);
- // If we're expecting anything, we must be expecting additions.
- if (!mExpectingAdditions) {
- fail("Can't add requests while expecting requests to be removed");
- }
- if (expectedScore != score) {
- fail("Expected score was " + expectedScore + " but actual was " + score
- + " in added request");
- }
-
- // Add the request.
- mNetworkRequests.put(request.requestId, request);
- super.handleAddRequest(request, score, factorySerialNumber);
- mExpectations.notify();
- }
+ mNetworkRequests.put(request.requestId, request);
+ super.handleAddRequest(request, score, factorySerialNumber);
+ mRequestHistory.add(new RequestEntry.Add(request, factorySerialNumber));
}
@Override
protected void handleRemoveRequest(NetworkRequest request) {
- synchronized (mExpectations) {
- final Integer expectedScore = mExpectations.poll(); // null if the queue is empty
+ mNetworkRequests.remove(request.requestId);
+ super.handleRemoveRequest(request);
+ mRequestHistory.add(new RequestEntry.Remove(request));
+ }
- assertTrue("Removed more requests than expected", expectedScore != null);
- // If we're expecting anything, we must be expecting removals.
- if (mExpectingAdditions) {
- fail("Can't remove requests while expecting requests to be added");
- }
+ public void assertRequestCountEquals(final int count) {
+ assertEquals(count, getMyRequestCount());
+ }
- // Remove the request.
- mNetworkRequests.remove(request.requestId);
- super.handleRemoveRequest(request);
- mExpectations.notify();
- }
+ @Override
+ public void terminate() {
+ super.terminate();
+ // Make sure there are no remaining requests unaccounted for.
+ assertNull(mRequestHistory.poll(TIMEOUT_MS, r -> true));
}
// Trigger releasing the request as unfulfillable
public void triggerUnfulfillable(NetworkRequest r) {
super.releaseRequestAsUnfulfillableByAnyFactory(r);
}
-
- private void assertNoExpectations() {
- if (mExpectations.size() != 0) {
- fail("Can't add expectation, " + mExpectations.size() + " already pending");
- }
- }
-
- // Expects that requests with the specified scores will be added.
- public void expectAddRequestsWithScores(final int... scores) {
- assertNoExpectations();
- mExpectingAdditions = true;
- for (int score : scores) {
- mExpectations.add(score);
- }
- }
-
- // Expects that count requests will be removed.
- public void expectRemoveRequests(final int count) {
- assertNoExpectations();
- mExpectingAdditions = false;
- for (int i = 0; i < count; ++i) {
- mExpectations.add(0); // For removals the score is ignored so any value will do.
- }
- }
-
- // Waits for the expected request additions or removals to happen within a timeout.
- public void waitForRequests() throws InterruptedException {
- final long deadline = SystemClock.elapsedRealtime() + TIMEOUT_MS;
- synchronized (mExpectations) {
- while (mExpectations.size() > 0 && SystemClock.elapsedRealtime() < deadline) {
- mExpectations.wait(deadline - SystemClock.elapsedRealtime());
- }
- }
- final long count = mExpectations.size();
- final String msg = count + " requests still not " +
- (mExpectingAdditions ? "added" : "removed") +
- " after " + TIMEOUT_MS + " ms";
- assertEquals(msg, 0, count);
- }
-
- public SparseArray<NetworkRequest> waitForNetworkRequests(final int count)
- throws InterruptedException {
- waitForRequests();
- assertEquals(count, getMyRequestCount());
- return mNetworkRequests;
- }
}
private Set<UidRange> uidRangesForUid(int uid) {
@@ -2595,12 +2575,6 @@
callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
}
- private int[] makeIntArray(final int size, final int value) {
- final int[] array = new int[size];
- Arrays.fill(array, value);
- return array;
- }
-
private void tryNetworkFactoryRequests(int capability) throws Exception {
// Verify NOT_RESTRICTED is set appropriately
final NetworkCapabilities nc = new NetworkRequest.Builder().addCapability(capability)
@@ -2622,9 +2596,9 @@
mServiceContext, "testFactory", filter);
testFactory.setScoreFilter(40);
ConditionVariable cv = testFactory.getNetworkStartedCV();
- testFactory.expectAddRequestsWithScores(0);
testFactory.register();
- testFactory.waitForNetworkRequests(1);
+ testFactory.expectRequestAdd();
+ testFactory.assertRequestCountEquals(1);
int expectedRequestCount = 1;
NetworkCallback networkCallback = null;
// For non-INTERNET capabilities we cannot rely on the default request being present, so
@@ -2633,13 +2607,12 @@
assertFalse(testFactory.getMyStartRequested());
NetworkRequest request = new NetworkRequest.Builder().addCapability(capability).build();
networkCallback = new NetworkCallback();
- testFactory.expectAddRequestsWithScores(0); // New request
mCm.requestNetwork(request, networkCallback);
expectedRequestCount++;
- testFactory.waitForNetworkRequests(expectedRequestCount);
+ testFactory.expectRequestAdd();
}
waitFor(cv);
- assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
+ testFactory.assertRequestCountEquals(expectedRequestCount);
assertTrue(testFactory.getMyStartRequested());
// Now bring in a higher scored network.
@@ -2653,15 +2626,14 @@
// When testAgent connects, ConnectivityService will re-send us all current requests with
// the new score. There are expectedRequestCount such requests, and we must wait for all of
// them.
- testFactory.expectAddRequestsWithScores(makeIntArray(expectedRequestCount, 50));
testAgent.connect(false);
testAgent.addCapability(capability);
waitFor(cv);
- testFactory.waitForNetworkRequests(expectedRequestCount);
+ testFactory.expectRequestAdds(expectedRequestCount);
+ testFactory.assertRequestCountEquals(expectedRequestCount);
assertFalse(testFactory.getMyStartRequested());
// Bring in a bunch of requests.
- testFactory.expectAddRequestsWithScores(makeIntArray(10, 50));
assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
ConnectivityManager.NetworkCallback[] networkCallbacks =
new ConnectivityManager.NetworkCallback[10];
@@ -2671,24 +2643,24 @@
builder.addCapability(capability);
mCm.requestNetwork(builder.build(), networkCallbacks[i]);
}
- testFactory.waitForNetworkRequests(10 + expectedRequestCount);
+ testFactory.expectRequestAdds(10);
+ testFactory.assertRequestCountEquals(10 + expectedRequestCount);
assertFalse(testFactory.getMyStartRequested());
// Remove the requests.
- testFactory.expectRemoveRequests(10);
for (int i = 0; i < networkCallbacks.length; i++) {
mCm.unregisterNetworkCallback(networkCallbacks[i]);
}
- testFactory.waitForNetworkRequests(expectedRequestCount);
+ testFactory.expectRequestRemoves(10);
+ testFactory.assertRequestCountEquals(expectedRequestCount);
assertFalse(testFactory.getMyStartRequested());
// Drop the higher scored network.
cv = testFactory.getNetworkStartedCV();
- // With the default network disconnecting, the requests are sent with score 0 to factories.
- testFactory.expectAddRequestsWithScores(makeIntArray(expectedRequestCount, 0));
testAgent.disconnect();
waitFor(cv);
- testFactory.waitForNetworkRequests(expectedRequestCount);
+ testFactory.expectRequestAdds(expectedRequestCount);
+ testFactory.assertRequestCountEquals(expectedRequestCount);
assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
assertTrue(testFactory.getMyStartRequested());
@@ -2731,9 +2703,8 @@
final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
mServiceContext, "testFactory", filter);
// Register the factory and don't be surprised when the default request arrives.
- testFactory.expectAddRequestsWithScores(0);
testFactory.register();
- testFactory.waitForNetworkRequests(1);
+ testFactory.expectRequestAdd();
testFactory.setScoreFilter(42);
testFactory.terminate();
@@ -3876,38 +3847,37 @@
testFactory.setScoreFilter(40);
// Register the factory and expect it to start looking for a network.
- testFactory.expectAddRequestsWithScores(0); // Score 0 as the request is not served yet.
testFactory.register();
try {
- testFactory.waitForNetworkRequests(1);
+ testFactory.expectRequestAdd();
+ testFactory.assertRequestCountEquals(1);
assertTrue(testFactory.getMyStartRequested());
// Bring up wifi. The factory stops looking for a network.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
// Score 60 - 40 penalty for not validated yet, then 60 when it validates
- testFactory.expectAddRequestsWithScores(20, 60);
mWiFiNetworkAgent.connect(true);
- testFactory.waitForRequests();
+ // Default request and mobile always on request
+ testFactory.expectRequestAdds(2);
assertFalse(testFactory.getMyStartRequested());
- ContentResolver cr = mServiceContext.getContentResolver();
-
// Turn on mobile data always on. The factory starts looking again.
- testFactory.expectAddRequestsWithScores(0); // Always on requests comes up with score 0
setAlwaysOnNetworks(true);
- testFactory.waitForNetworkRequests(2);
+ testFactory.expectRequestAdd();
+ testFactory.assertRequestCountEquals(2);
+
assertTrue(testFactory.getMyStartRequested());
// Bring up cell data and check that the factory stops looking.
assertLength(1, mCm.getAllNetworks());
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- testFactory.expectAddRequestsWithScores(10, 50); // Unvalidated, then validated
mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
- testFactory.waitForNetworkRequests(2);
- assertFalse(
- testFactory.getMyStartRequested()); // Because the cell network outscores us.
+ testFactory.expectRequestAdds(2); // Unvalidated and validated
+ testFactory.assertRequestCountEquals(2);
+ // The cell network outscores the factory filter, so start is not requested.
+ assertFalse(testFactory.getMyStartRequested());
// Check that cell data stays up.
waitForIdle();
@@ -3915,9 +3885,8 @@
assertLength(2, mCm.getAllNetworks());
// Turn off mobile data always on and expect the request to disappear...
- testFactory.expectRemoveRequests(1);
setAlwaysOnNetworks(false);
- testFactory.waitForNetworkRequests(1);
+ testFactory.expectRequestRemove();
// ... and cell data to be torn down.
cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
@@ -4224,46 +4193,33 @@
testFactory.setScoreFilter(40);
// Register the factory and expect it to receive the default request.
- testFactory.expectAddRequestsWithScores(0);
testFactory.register();
- SparseArray<NetworkRequest> requests = testFactory.waitForNetworkRequests(1);
-
- assertEquals(1, requests.size()); // have 1 request at this point
- int origRequestId = requests.valueAt(0).requestId;
+ testFactory.expectRequestAdd();
// Now file the test request and expect it.
- testFactory.expectAddRequestsWithScores(0);
mCm.requestNetwork(nr, networkCallback);
- requests = testFactory.waitForNetworkRequests(2); // have 2 requests at this point
+ final NetworkRequest newRequest = testFactory.expectRequestAdd().request;
- int newRequestId = 0;
- for (int i = 0; i < requests.size(); ++i) {
- if (requests.valueAt(i).requestId != origRequestId) {
- newRequestId = requests.valueAt(i).requestId;
- break;
- }
- }
-
- testFactory.expectRemoveRequests(1);
if (preUnregister) {
mCm.unregisterNetworkCallback(networkCallback);
// Simulate the factory releasing the request as unfulfillable: no-op since
// the callback has already been unregistered (but a test that no exceptions are
// thrown).
- testFactory.triggerUnfulfillable(requests.get(newRequestId));
+ testFactory.triggerUnfulfillable(newRequest);
} else {
// Simulate the factory releasing the request as unfulfillable and expect onUnavailable!
- testFactory.triggerUnfulfillable(requests.get(newRequestId));
+ testFactory.triggerUnfulfillable(newRequest);
networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null);
- testFactory.waitForRequests();
// unregister network callback - a no-op (since already freed by the
// on-unavailable), but should not fail or throw exceptions.
mCm.unregisterNetworkCallback(networkCallback);
}
+ testFactory.expectRequestRemove();
+
testFactory.terminate();
handlerThread.quit();
}
diff --git a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
index a10a3c8..e590fb7 100644
--- a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
+++ b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
@@ -55,7 +55,7 @@
private val supportedTypes = arrayOf(TYPE_MOBILE, TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_SUPL)
private val mMockService = mock(ConnectivityService::class.java).apply {
- doReturn(false).`when`(this).isDefaultNetwork(any())
+ doReturn(false).`when`(this).isFallbackNetwork(any())
}
private val mTracker = LegacyTypeTracker(mMockService).apply {
supportedTypes.forEach {
@@ -126,11 +126,11 @@
fun testBroadcastOnDisconnect() {
val mobileNai1 = mock(NetworkAgentInfo::class.java)
val mobileNai2 = mock(NetworkAgentInfo::class.java)
- doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai1)
+ doReturn(false).`when`(mMockService).isFallbackNetwork(mobileNai1)
mTracker.add(TYPE_MOBILE, mobileNai1)
verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, CONNECTED, TYPE_MOBILE)
reset(mMockService)
- doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai2)
+ doReturn(false).`when`(mMockService).isFallbackNetwork(mobileNai2)
mTracker.add(TYPE_MOBILE, mobileNai2)
verify(mMockService, never()).sendLegacyNetworkBroadcast(any(), any(), anyInt())
mTracker.remove(TYPE_MOBILE, mobileNai1, false /* wasDefault */)
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 68aaaed..f478282 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -148,6 +148,7 @@
managedProfileA.profileGroupId = primaryUser.id;
}
+ static final Network EGRESS_NETWORK = new Network(101);
static final String EGRESS_IFACE = "wlan0";
static final String TEST_VPN_PKG = "com.testvpn.vpn";
private static final String TEST_VPN_SERVER = "1.2.3.4";
@@ -963,7 +964,7 @@
InetAddresses.parseNumericAddress("192.0.2.0"), EGRESS_IFACE);
lp.addRoute(defaultRoute);
- vpn.startLegacyVpn(vpnProfile, mKeyStore, lp);
+ vpn.startLegacyVpn(vpnProfile, mKeyStore, EGRESS_NETWORK, lp);
return vpn;
}
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/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index e26bf19..e7d334e 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -22,7 +22,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -30,6 +29,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
@@ -39,16 +39,20 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
+import android.net.NetworkCapabilities.Transport;
+import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnConfigTest;
import android.net.vcn.VcnUnderlyingNetworkPolicy;
+import android.net.wifi.WifiInfo;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
@@ -253,7 +257,14 @@
verify(mConfigReadWriteHelper).readFromDisk();
}
- private void triggerSubscriptionTrackerCallback(Set<ParcelUuid> activeSubscriptionGroups) {
+ private TelephonySubscriptionSnapshot triggerSubscriptionTrackerCbAndGetSnapshot(
+ Set<ParcelUuid> activeSubscriptionGroups) {
+ return triggerSubscriptionTrackerCbAndGetSnapshot(
+ activeSubscriptionGroups, Collections.emptyMap());
+ }
+
+ private TelephonySubscriptionSnapshot triggerSubscriptionTrackerCbAndGetSnapshot(
+ Set<ParcelUuid> activeSubscriptionGroups, Map<Integer, ParcelUuid> subIdToGroupMap) {
final TelephonySubscriptionSnapshot snapshot = mock(TelephonySubscriptionSnapshot.class);
doReturn(activeSubscriptionGroups).when(snapshot).getActiveSubscriptionGroups();
@@ -267,8 +278,14 @@
argThat(val -> activeSubscriptionGroups.contains(val)),
eq(TEST_PACKAGE_NAME));
+ doAnswer(invocation -> {
+ return subIdToGroupMap.get(invocation.getArgument(0));
+ }).when(snapshot).getGroupForSubId(anyInt());
+
final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
cb.onNewSnapshot(snapshot);
+
+ return snapshot;
}
private TelephonySubscriptionTrackerCallback getTelephonySubscriptionTrackerCallback() {
@@ -287,7 +304,7 @@
@Test
public void testTelephonyNetworkTrackerCallbackStartsInstances() throws Exception {
- triggerSubscriptionTrackerCallback(Collections.singleton(TEST_UUID_1));
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
verify(mMockDeps).newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG));
}
@@ -296,7 +313,7 @@
final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
- triggerSubscriptionTrackerCallback(Collections.emptySet());
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet());
// Verify teardown after delay
mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
@@ -311,13 +328,13 @@
final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
// Simulate SIM unloaded
- triggerSubscriptionTrackerCallback(Collections.emptySet());
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet());
// Simulate new SIM loaded right during teardown delay.
mTestLooper.moveTimeForward(
VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2);
mTestLooper.dispatchAll();
- triggerSubscriptionTrackerCallback(Collections.singleton(TEST_UUID_2));
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_2));
// Verify that even after the full timeout duration, the VCN instance is not torn down
mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
@@ -331,7 +348,7 @@
final Vcn oldInstance = startAndGetVcnInstance(TEST_UUID_2);
// Simulate SIM unloaded
- triggerSubscriptionTrackerCallback(Collections.emptySet());
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet());
// Config cleared, SIM reloaded & config re-added right before teardown delay, staring new
// vcnInstance.
@@ -496,14 +513,73 @@
mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
}
+ private void setUpVcnSubscription(int subId, ParcelUuid subGroup) {
+ mVcnMgmtSvc.setVcnConfig(subGroup, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+
+ triggerSubscriptionTrackerCbAndGetSnapshot(
+ Collections.singleton(subGroup), Collections.singletonMap(subId, subGroup));
+ }
+
+ private void verifyMergedNetworkCapabilities(
+ NetworkCapabilities mergedCapabilities, @Transport int transportType) {
+ assertTrue(mergedCapabilities.hasTransport(transportType));
+ assertFalse(
+ mergedCapabilities.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED));
+ }
+
@Test
- public void testGetUnderlyingNetworkPolicy() throws Exception {
+ public void testGetUnderlyingNetworkPolicyTransportCell() throws Exception {
+ setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2);
+
+ NetworkCapabilities nc =
+ new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID))
+ .build();
+
VcnUnderlyingNetworkPolicy policy =
- mVcnMgmtSvc.getUnderlyingNetworkPolicy(
- new NetworkCapabilities(), new LinkProperties());
+ mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
assertFalse(policy.isTeardownRequested());
- assertNotNull(policy.getMergedNetworkCapabilities());
+ verifyMergedNetworkCapabilities(
+ policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_CELLULAR);
+ }
+
+ @Test
+ public void testGetUnderlyingNetworkPolicyTransportWifi() throws Exception {
+ setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2);
+
+ WifiInfo wifiInfo = mock(WifiInfo.class);
+ when(wifiInfo.makeCopy(anyBoolean())).thenReturn(wifiInfo);
+ when(mMockDeps.getSubIdForWifiInfo(eq(wifiInfo))).thenReturn(TEST_SUBSCRIPTION_ID);
+ NetworkCapabilities nc =
+ new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .setTransportInfo(wifiInfo)
+ .build();
+
+ VcnUnderlyingNetworkPolicy policy =
+ mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
+
+ assertFalse(policy.isTeardownRequested());
+ verifyMergedNetworkCapabilities(
+ policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_WIFI);
+ }
+
+ @Test
+ public void testGetUnderlyingNetworkPolicyNonVcnNetwork() throws Exception {
+ NetworkCapabilities nc =
+ new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID))
+ .build();
+
+ VcnUnderlyingNetworkPolicy policy =
+ mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
+
+ assertFalse(policy.isTeardownRequested());
+ assertEquals(nc, policy.getMergedNetworkCapabilities());
}
@Test(expected = SecurityException.class)
diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
new file mode 100644
index 0000000..48e068d
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+import static com.android.server.vcn.VcnTestUtils.setupSystemService;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.TelephonyNetworkSpecifier;
+import android.os.ParcelUuid;
+import android.os.test.TestLooper;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+
+import com.android.server.vcn.UnderlyingNetworkTracker.NetworkBringupCallback;
+import com.android.server.vcn.UnderlyingNetworkTracker.RouteSelectionCallback;
+import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
+import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
+public class UnderlyingNetworkTrackerTest {
+ private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
+ private static final int INITIAL_SUB_ID_1 = 1;
+ private static final int INITIAL_SUB_ID_2 = 2;
+
+ private static final NetworkCapabilities INITIAL_NETWORK_CAPABILITIES =
+ new NetworkCapabilities.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .build();
+ private static final NetworkCapabilities SUSPENDED_NETWORK_CAPABILITIES =
+ new NetworkCapabilities.Builder(INITIAL_NETWORK_CAPABILITIES)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
+ .build();
+ private static final NetworkCapabilities UPDATED_NETWORK_CAPABILITIES =
+ new NetworkCapabilities.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .build();
+
+ private static final LinkProperties INITIAL_LINK_PROPERTIES =
+ getLinkPropertiesWithName("initial_iface");
+ private static final LinkProperties UPDATED_LINK_PROPERTIES =
+ getLinkPropertiesWithName("updated_iface");
+
+ @Mock private Context mContext;
+ @Mock private VcnNetworkProvider mVcnNetworkProvider;
+ @Mock private ConnectivityManager mConnectivityManager;
+ @Mock private SubscriptionManager mSubscriptionManager;
+ @Mock private UnderlyingNetworkTrackerCallback mNetworkTrackerCb;
+ @Mock private Network mNetwork;
+
+ @Captor private ArgumentCaptor<RouteSelectionCallback> mRouteSelectionCallbackCaptor;
+
+ private TestLooper mTestLooper;
+ private VcnContext mVcnContext;
+ private UnderlyingNetworkTracker mUnderlyingNetworkTracker;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mTestLooper = new TestLooper();
+ mVcnContext = spy(new VcnContext(mContext, mTestLooper.getLooper(), mVcnNetworkProvider));
+ doNothing().when(mVcnContext).ensureRunningOnLooperThread();
+
+ setupSystemService(
+ mContext,
+ mConnectivityManager,
+ Context.CONNECTIVITY_SERVICE,
+ ConnectivityManager.class);
+ setupSystemService(
+ mContext,
+ mSubscriptionManager,
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE,
+ SubscriptionManager.class);
+
+ List<SubscriptionInfo> initialSubInfos =
+ Arrays.asList(
+ getSubscriptionInfoForSubId(INITIAL_SUB_ID_1),
+ getSubscriptionInfoForSubId(INITIAL_SUB_ID_2));
+ when(mSubscriptionManager.getSubscriptionsInGroup(eq(SUB_GROUP)))
+ .thenReturn(initialSubInfos);
+
+ mUnderlyingNetworkTracker =
+ new UnderlyingNetworkTracker(
+ mVcnContext,
+ SUB_GROUP,
+ Collections.singleton(NetworkCapabilities.NET_CAPABILITY_INTERNET),
+ mNetworkTrackerCb);
+ }
+
+ private static LinkProperties getLinkPropertiesWithName(String iface) {
+ LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setInterfaceName(iface);
+ return linkProperties;
+ }
+
+ private SubscriptionInfo getSubscriptionInfoForSubId(int subId) {
+ SubscriptionInfo subInfo = mock(SubscriptionInfo.class);
+ when(subInfo.getSubscriptionId()).thenReturn(subId);
+ return subInfo;
+ }
+
+ @Test
+ public void testNetworkCallbacksRegisteredOnStartup() {
+ // verify NetworkCallbacks registered when instantiated
+ verify(mConnectivityManager)
+ .requestBackgroundNetwork(
+ eq(getWifiRequest()),
+ any(),
+ any(NetworkBringupCallback.class));
+ verify(mConnectivityManager)
+ .requestBackgroundNetwork(
+ eq(getCellRequestForSubId(INITIAL_SUB_ID_1)),
+ any(),
+ any(NetworkBringupCallback.class));
+ verify(mConnectivityManager)
+ .requestBackgroundNetwork(
+ eq(getCellRequestForSubId(INITIAL_SUB_ID_2)),
+ any(),
+ any(NetworkBringupCallback.class));
+ verify(mConnectivityManager)
+ .requestBackgroundNetwork(
+ eq(getRouteSelectionRequest()),
+ any(),
+ any(RouteSelectionCallback.class));
+
+ verify(mSubscriptionManager).getSubscriptionsInGroup(eq(SUB_GROUP));
+ }
+
+ private NetworkRequest getWifiRequest() {
+ return getExpectedRequestBase()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .build();
+ }
+
+ private NetworkRequest getCellRequestForSubId(int subId) {
+ return getExpectedRequestBase()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId))
+ .build();
+ }
+
+ private NetworkRequest getRouteSelectionRequest() {
+ return getExpectedRequestBase().build();
+ }
+
+ private NetworkRequest.Builder getExpectedRequestBase() {
+ return new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
+ .addUnwantedCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+ }
+
+ @Test
+ public void testTeardown() {
+ mUnderlyingNetworkTracker.teardown();
+
+ // Expect 3 NetworkBringupCallbacks to be unregistered: 1 for WiFi and 2 for Cellular (1x
+ // for each subId)
+ verify(mConnectivityManager, times(3))
+ .unregisterNetworkCallback(any(NetworkBringupCallback.class));
+ verify(mConnectivityManager).unregisterNetworkCallback(any(RouteSelectionCallback.class));
+ }
+
+ @Test
+ public void testUnderlyingNetworkRecordEquals() {
+ UnderlyingNetworkRecord recordA =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ INITIAL_NETWORK_CAPABILITIES,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */);
+ UnderlyingNetworkRecord recordB =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ INITIAL_NETWORK_CAPABILITIES,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */);
+ UnderlyingNetworkRecord recordC =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ UPDATED_NETWORK_CAPABILITIES,
+ UPDATED_LINK_PROPERTIES,
+ false /* isBlocked */);
+
+ assertEquals(recordA, recordB);
+ assertNotEquals(recordA, recordC);
+ }
+
+ @Test
+ public void testRecordTrackerCallbackNotifiedForNetworkChange() {
+ verifyRegistrationOnAvailableAndGetCallback();
+ }
+
+ private RouteSelectionCallback verifyRegistrationOnAvailableAndGetCallback() {
+ return verifyRegistrationOnAvailableAndGetCallback(INITIAL_NETWORK_CAPABILITIES);
+ }
+
+ private RouteSelectionCallback verifyRegistrationOnAvailableAndGetCallback(
+ NetworkCapabilities networkCapabilities) {
+ verify(mConnectivityManager)
+ .requestBackgroundNetwork(
+ eq(getRouteSelectionRequest()),
+ any(),
+ mRouteSelectionCallbackCaptor.capture());
+
+ RouteSelectionCallback cb = mRouteSelectionCallbackCaptor.getValue();
+ cb.onAvailable(mNetwork);
+ cb.onCapabilitiesChanged(mNetwork, networkCapabilities);
+ cb.onLinkPropertiesChanged(mNetwork, INITIAL_LINK_PROPERTIES);
+ cb.onBlockedStatusChanged(mNetwork, false /* isFalse */);
+
+ UnderlyingNetworkRecord expectedRecord =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ networkCapabilities,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */);
+ verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ return cb;
+ }
+
+ @Test
+ public void testRecordTrackerCallbackNotifiedForNetworkCapabilitiesChange() {
+ RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+
+ cb.onCapabilitiesChanged(mNetwork, UPDATED_NETWORK_CAPABILITIES);
+
+ UnderlyingNetworkRecord expectedRecord =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ UPDATED_NETWORK_CAPABILITIES,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */);
+ verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ }
+
+ @Test
+ public void testRecordTrackerCallbackNotifiedForLinkPropertiesChange() {
+ RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+
+ cb.onLinkPropertiesChanged(mNetwork, UPDATED_LINK_PROPERTIES);
+
+ UnderlyingNetworkRecord expectedRecord =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ INITIAL_NETWORK_CAPABILITIES,
+ UPDATED_LINK_PROPERTIES,
+ false /* isBlocked */);
+ verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ }
+
+ @Test
+ public void testRecordTrackerCallbackNotifiedForNetworkSuspended() {
+ RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+
+ cb.onNetworkSuspended(mNetwork);
+
+ UnderlyingNetworkRecord expectedRecord =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ SUSPENDED_NETWORK_CAPABILITIES,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */);
+ verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ }
+
+ @Test
+ public void testRecordTrackerCallbackNotifiedForNetworkResumed() {
+ RouteSelectionCallback cb =
+ verifyRegistrationOnAvailableAndGetCallback(SUSPENDED_NETWORK_CAPABILITIES);
+
+ cb.onNetworkResumed(mNetwork);
+
+ UnderlyingNetworkRecord expectedRecord =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ INITIAL_NETWORK_CAPABILITIES,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */);
+ verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ }
+
+ @Test
+ public void testRecordTrackerCallbackNotifiedForBlocked() {
+ RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+
+ cb.onBlockedStatusChanged(mNetwork, true /* isBlocked */);
+
+ UnderlyingNetworkRecord expectedRecord =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ INITIAL_NETWORK_CAPABILITIES,
+ INITIAL_LINK_PROPERTIES,
+ true /* isBlocked */);
+ verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ }
+
+ @Test
+ public void testRecordTrackerCallbackNotifiedForNetworkLoss() {
+ RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+
+ cb.onLost(mNetwork);
+
+ verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(null);
+ }
+
+ @Test
+ public void testRecordTrackerCallbackIgnoresDuplicateRecord() {
+ RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+
+ cb.onCapabilitiesChanged(mNetwork, INITIAL_NETWORK_CAPABILITIES);
+
+ // Verify no more calls to the UnderlyingNetworkTrackerCallback when the
+ // UnderlyingNetworkRecord does not actually change
+ verifyNoMoreInteractions(mNetworkTrackerCb);
+ }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index b4d39bf..4d92fb9 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -94,7 +94,7 @@
doReturn(mUnderlyingNetworkTracker)
.when(mDeps)
- .newUnderlyingNetworkTracker(any(), any(), any());
+ .newUnderlyingNetworkTracker(any(), any(), any(), any());
}
@Before
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()