diff options
706 files changed, 20990 insertions, 11297 deletions
diff --git a/apct-tests/perftests/contentcapture/Android.bp b/apct-tests/perftests/contentcapture/Android.bp index 19a66ad9f27b..638403d40ea3 100644 --- a/apct-tests/perftests/contentcapture/Android.bp +++ b/apct-tests/perftests/contentcapture/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test { name: "ContentCapturePerfTests", srcs: ["src/**/*.java"], diff --git a/apct-tests/perftests/inputmethod/Android.bp b/apct-tests/perftests/inputmethod/Android.bp index 463ac9b8b0c8..f2f1f758112e 100644 --- a/apct-tests/perftests/inputmethod/Android.bp +++ b/apct-tests/perftests/inputmethod/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test { name: "ImePerfTests", srcs: ["src/**/*.java"], diff --git a/apex/appsearch/Android.bp b/apex/appsearch/Android.bp index b014fdcb3df3..87f65a922004 100644 --- a/apex/appsearch/Android.bp +++ b/apex/appsearch/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + apex { name: "com.android.appsearch", manifest: "apex_manifest.json", @@ -21,6 +30,7 @@ apex { ], key: "com.android.appsearch.key", certificate: ":com.android.appsearch.certificate", + updatable: false, } apex_key { diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp index 8ba79541088b..5def55fd31ff 100644 --- a/apex/appsearch/framework/Android.bp +++ b/apex/appsearch/framework/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + filegroup { name: "framework-appsearch-sources", srcs: [ diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt index 905000ac7c3d..cc79f6bf9682 100644 --- a/apex/appsearch/framework/api/current.txt +++ b/apex/appsearch/framework/api/current.txt @@ -2,6 +2,7 @@ package android.app.appsearch { public final class AppSearchBatchResult<KeyType, ValueType> { + method @NonNull public java.util.Map<KeyType,android.app.appsearch.AppSearchResult<ValueType>> getAll(); method @NonNull public java.util.Map<KeyType,android.app.appsearch.AppSearchResult<ValueType>> getFailures(); method @NonNull public java.util.Map<KeyType,ValueType> getSuccesses(); method public boolean isSuccess(); @@ -146,7 +147,7 @@ package android.app.appsearch { 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 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>>); } @@ -358,7 +359,6 @@ package android.app.appsearch { method @NonNull public java.util.Set<java.lang.String> getIncompatibleTypes(); method @NonNull public java.util.Set<java.lang.String> getMigratedTypes(); method @NonNull public java.util.List<android.app.appsearch.SetSchemaResponse.MigrationFailure> getMigrationFailures(); - method public boolean isSuccess(); } public static class SetSchemaResponse.MigrationFailure { diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java index cd75b1456ba8..31ab259127c3 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java @@ -28,11 +28,19 @@ import java.util.Collections; import java.util.Map; /** - * Provides access to multiple {@link AppSearchResult}s from a batch operation accepting multiple - * inputs. + * Provides results for AppSearch batch operations which encompass multiple documents. * - * @param <KeyType> The type of the keys for {@link #getSuccesses} and {@link #getFailures}. - * @param <ValueType> The type of result objects associated with the keys. + * <p>Individual results of a batch operation are separated into two maps: one for successes and one + * for failures. For successes, {@link #getSuccesses()} will return a map of keys to instances of + * the value type. For failures, {@link #getFailures()} will return a map of keys to {@link + * AppSearchResult} objects. + * + * <p>Alternatively, {@link #getAll()} returns a map of keys to {@link AppSearchResult} objects for + * both successes and failures. + * + * @see AppSearchSession#put + * @see AppSearchSession#getByUri + * @see AppSearchSession#remove */ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelable { @NonNull private final Map<KeyType, ValueType> mSuccesses; @@ -75,8 +83,11 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl } /** - * Returns a {@link Map} of all successful keys mapped to the successful {@link - * AppSearchResult}s they produced. + * Returns a {@link Map} of keys mapped to instances of the value type for all successful + * individual results. + * + * <p>Example: {@link AppSearchSession#getByUri} returns an {@link AppSearchBatchResult}. Each + * key (a URI of {@code String} type) will map to a {@link GenericDocument} object. * * <p>The values of the {@link Map} will not be {@code null}. */ @@ -86,8 +97,8 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl } /** - * Returns a {@link Map} of all failed keys mapped to the failed {@link AppSearchResult}s they - * produced. + * Returns a {@link Map} of keys mapped to instances of {@link AppSearchResult} for all failed + * individual results. * * <p>The values of the {@link Map} will not be {@code null}. */ @@ -97,10 +108,10 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl } /** - * Returns a {@link Map} of all keys mapped to the {@link AppSearchResult}s they produced. + * Returns a {@link Map} of keys mapped to instances of {@link AppSearchResult} for all + * individual results. * * <p>The values of the {@link Map} will not be {@code null}. - * @hide */ @NonNull public Map<KeyType, AppSearchResult<ValueType>> getAll() { @@ -150,8 +161,8 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl /** * Builder for {@link AppSearchBatchResult} objects. * - * @param <KeyType> The type of keys. - * @param <ValueType> The type of result objects associated with the keys. + * <p>Once {@link #build} is called, the instance can no longer be used. + * * @hide */ public static final class Builder<KeyType, ValueType> { @@ -161,9 +172,11 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl private boolean mBuilt = false; /** - * Associates the {@code key} with the given successful return value. + * Associates the {@code key} with the provided successful return value. * * <p>Any previous mapping for a key, whether success or failure, is deleted. + * + * @throws IllegalStateException if the builder has already been used. */ @NonNull public Builder<KeyType, ValueType> setSuccess( @@ -174,9 +187,11 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl } /** - * Associates the {@code key} with the given failure code and error message. + * Associates the {@code key} with the provided failure code and error message. * * <p>Any previous mapping for a key, whether success or failure, is deleted. + * + * @throws IllegalStateException if the builder has already been used. */ @NonNull public Builder<KeyType, ValueType> setFailure( @@ -189,9 +204,11 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl } /** - * Associates the {@code key} with the given {@code result}. + * Associates the {@code key} with the provided {@code result}. * * <p>Any previous mapping for a key, whether success or failure, is deleted. + * + * @throws IllegalStateException if the builder has already been used. */ @NonNull public Builder<KeyType, ValueType> setResult( @@ -210,7 +227,11 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl return this; } - /** Builds an {@link AppSearchBatchResult} from the contents of this {@link Builder}. */ + /** + * Builds an {@link AppSearchBatchResult} object from the contents of this {@link Builder}. + * + * @throws IllegalStateException if the builder has already been used. + */ @NonNull public AppSearchBatchResult<KeyType, ValueType> build() { Preconditions.checkState(!mBuilt, "Builder has already been used"); diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java index 69d4e536597f..6a5975ef7ff7 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java @@ -36,12 +36,89 @@ import java.util.concurrent.Executor; import java.util.function.Consumer; /** - * This class provides access to the centralized AppSearch index maintained by the system. + * Provides access to the centralized AppSearch index maintained by the system. * - * <p>Apps can index structured text documents with AppSearch, which can then be retrieved through - * the query API. + * <p>AppSearch is a search library for managing structured data featuring: + * <ul> + * <li>A fully offline on-device solution + * <li>A set of APIs for applications to index documents and retrieve them via full-text search + * <li>APIs for applications to allow the System to display their content on system UI surfaces + * <li>Similarly, APIs for applications to allow the System to share their content with other + * specified applications. + * </ul> + * + * <p>Applications create a database by opening an {@link AppSearchSession}. + * + * <p>Example: + * <pre> + * AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class); + * + * AppSearchManager.SearchContext searchContext = new AppSearchManager.SearchContext.Builder(). + * setDatabaseName(dbName).build()); + * appSearchManager.createSearchSession(searchContext, mExecutor, appSearchSessionResult -> { + * mAppSearchSession = appSearchSessionResult.getResultValue(); + * });</pre> + * + * <p>After opening the session, a schema must be set in order to define the organizational + * structure of data. The schema is set by calling {@link AppSearchSession#setSchema}. The schema + * is composed of a collection of {@link AppSearchSchema} objects, each of which defines a unique + * type of data. + * + * <p>Example: + * <pre> + * AppSearchSchema emailSchemaType = new AppSearchSchema.Builder("Email") + * .addProperty(new StringPropertyConfig.Builder("subject") + * .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + * .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + * .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + * .build() + * ).build(); + * + * SetSchemaRequest request = new SetSchemaRequest.Builder().addSchema(emailSchemaType).build(); + * mAppSearchSession.set(request, mExecutor, appSearchResult -> { + * if (appSearchResult.isSuccess()) { + * //Schema has been successfully set. + * } + * });</pre> + * + * <p>The basic unit of data in AppSearch is represented as a {@link GenericDocument} object, + * containing a URI, namespace, time-to-live, score, and properties. A namespace organizes a + * logical group of documents. For example, a namespace can be created to group documents on a + * per-account basis. A URI identifies a single document within a namespace. The combination + * of URI and namespace uniquely identifies a {@link GenericDocument} in the database. + * + * <p>Once the schema has been set, {@link GenericDocument} objects can be put into the database + * and indexed by calling {@link AppSearchSession#put}. + * + * <p>Example: + * <pre> + * // Although for this example we use GenericDocument directly, we recommend extending + * // GenericDocument to create specific types (i.e. Email) with specific setters/getters. + * GenericDocument email = new GenericDocument.Builder<>(URI, EMAIL_SCHEMA_TYPE) + * .setNamespace(NAMESPACE) + * .setPropertyString(“subject”, EMAIL_SUBJECT) + * .setScore(EMAIL_SCORE) + * .build(); + * + * PutDocumentsRequest request = new PutDocumentsRequest.Builder().addGenericDocuments(email) + * .build(); + * mAppSearchSession.put(request, mExecutor, appSearchBatchResult -> { + * if (appSearchBatchResult.isSuccess()) { + * //All documents have been successfully indexed. + * } + * });</pre> + * + * <p>Searching within the database is done by calling {@link AppSearchSession#search} and providing + * the query string to search for, as well as a {@link SearchSpec}. + * + * <p>Alternatively, {@link AppSearchSession#getByUri} can be called to retrieve documents by URI + * and namespace. + * + * <p>Document removal is done either by time-to-live expiration, or explicitly calling a remove + * operation. Remove operations can be done by URI and namespace via + * {@link AppSearchSession#remove(RemoveByUriRequest, Executor, BatchResultCallback)}, + * or by query via {@link AppSearchSession#remove(String, SearchSpec, Executor, Consumer)}. */ -// TODO(b/148046169): This class header needs a detailed example/tutorial. @SystemService(Context.APP_SEARCH_SERVICE) public class AppSearchManager { /** diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java index 73ca0ccab46d..54f33509072b 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java @@ -41,7 +41,7 @@ import java.util.function.Consumer; * Represents a connection to an AppSearch storage system where {@link GenericDocument}s can be * placed and queried. * - * This class is thread safe. + * <p>This class is thread safe. */ public final class AppSearchSession implements Closeable { private static final String TAG = "AppSearchSession"; @@ -102,92 +102,23 @@ public final class AppSearchSession implements Closeable { } /** - * 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 - * types of schema modifications are always safe and are made without deleting any existing - * documents: - * - * <ul> - * <li>Addition of new types - * <li>Addition of new {@link AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} or - * {@link AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED} properties to a - * type - * <li>Changing the cardinality of a data type to be less restrictive (e.g. changing an {@link - * AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a {@link - * AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED} property. - * </ul> - * - * <p>The following types of schema changes are not backwards-compatible: - * - * <ul> - * <li>Removal of an existing type - * <li>Removal of a property from a type - * <li>Changing the data type ({@code boolean}, {@code long}, etc.) of an existing property - * <li>For properties of {@code Document} type, changing the schema type of {@code Document}s - * of that property - * <li>Changing the cardinality of a data type to be more restrictive (e.g. changing an {@link - * AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a {@link - * AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property). - * <li>Adding a {@link AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property. - * </ul> - * - * <p>Supplying a schema with such changes will, by default, result in this call completing its - * future with an {@link android.app.appsearch.exceptions.AppSearchException} with a code of - * {@link AppSearchResult#RESULT_INVALID_SCHEMA} and a message describing the incompatibility. - * In this case the previously set schema will remain active. - * - * <p>If you need to make non-backwards-compatible changes as described above, you can either: - * - * <ul> - * <li>Set the {@link SetSchemaRequest.Builder#setForceOverride} method to {@code true}. In - * this case, instead of completing its future with an {@link - * android.app.appsearch.exceptions.AppSearchException} with the {@link - * AppSearchResult#RESULT_INVALID_SCHEMA} error code, all documents which are not - * compatible with the new schema will be deleted and the incompatible schema will be - * applied. Incompatible types and deleted types will be set into {@link - * SetSchemaResponse#getIncompatibleTypes()} and {@link - * SetSchemaResponse#getDeletedTypes()}, respectively. - * <li>Add a {@link android.app.appsearch.AppSearchSchema.Migrator} for each incompatible type - * and make no deletion. The migrator will migrate documents from it's old schema version - * to the new version. Migrated types will be set into both {@link - * SetSchemaResponse#getIncompatibleTypes()} and {@link - * SetSchemaResponse#getMigratedTypes()}. See the migration section below. - * </ul> - * - * <p>It is a no-op to set the same schema as has been previously set; this is handled - * efficiently. - * - * <p>By default, documents are visible on platform surfaces. To opt out, call {@code - * SetSchemaRequest.Builder#setPlatformSurfaceable} with {@code surfaceable} as false. Any - * visibility settings apply only to the schemas that are included in the {@code request}. - * Visibility settings for a schema type do not apply or persist across {@link - * SetSchemaRequest}s. - * - * <p>Migration: make non-backwards-compatible changes will delete all stored documents in old - * schema. You can save your documents by setting {@link - * android.app.appsearch.AppSearchSchema.Migrator} via the {@link - * SetSchemaRequest.Builder#setMigrator} for each type you want to save. - * - * <p>{@link android.app.appsearch.AppSearchSchema.Migrator#onDowngrade} or {@link - * android.app.appsearch.AppSearchSchema.Migrator#onUpgrade} will be triggered if the version - * number of the schema stored in AppSearch is different with the version in the request. + * Sets the schema that represents the organizational structure of data within the AppSearch + * database. * - * <p>If any error or Exception occurred in the {@link - * android.app.appsearch.AppSearchSchema.Migrator#onDowngrade}, {@link - * android.app.appsearch.AppSearchSchema.Migrator#onUpgrade} or {@link - * android.app.appsearch.AppSearchMigrationHelper.Transformer#transform}, the migration will be - * terminated, the setSchema request will be rejected unless the schema changes are - * backwards-compatible, and stored documents won't have any observable changes. + * <p>Upon creating an {@link AppSearchSession}, {@link #setSchema} should be called. If the + * schema needs to be updated, or it has not been previously set, then the provided schema will + * be saved and persisted to disk. Otherwise, {@link #setSchema} is handled efficiently as a + * no-op call. * - * @param request The schema update request. + * @param request the schema to set or update the AppSearch database to. * @param executor Executor on which to invoke the callback. * @param callback Callback to receive errors resulting from setting the schema. If the * operation succeeds, the callback will be invoked with {@code null}. * @see android.app.appsearch.AppSearchSchema.Migrator * @see android.app.appsearch.AppSearchMigrationHelper.Transformer */ + // TODO(b/169883602): Change @code references to @link when setPlatformSurfaceable APIs are + // exposed. public void setSchema( @NonNull SetSchemaRequest request, @NonNull @CallbackExecutor Executor executor, @@ -280,12 +211,13 @@ public final class AppSearchSession implements Closeable { } /** - * Indexes documents into AppSearch. + * Indexes documents into the {@link AppSearchSession} database. * - * <p>Each {@link GenericDocument}'s {@code schemaType} field must be set to the name of a - * schema type previously registered via the {@link #setSchema} method. + * <p>Each {@link GenericDocument} object must have a {@code schemaType} field set to an {@link + * AppSearchSchema} type that has been previously registered by calling the {@link #setSchema} + * method. * - * @param request {@link PutDocumentsRequest} containing documents to be indexed + * @param request containing documents to be indexed. * @param executor Executor on which to invoke the callback. * @param callback Callback to receive pending result of performing this operation. The keys * of the returned {@link AppSearchBatchResult} are the URIs of the input @@ -497,7 +429,6 @@ public final class AppSearchSession implements Closeable { * @param callback Callback to receive errors. If the operation succeeds, the callback will be * invoked with {@code null}. */ - @NonNull public void reportUsage( @NonNull ReportUsageRequest request, @NonNull @CallbackExecutor Executor executor, diff --git a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java index 09bca4fb3b9d..93b102b864f4 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java +++ b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java @@ -98,8 +98,7 @@ public class GlobalSearchSession implements Closeable { * <p>Document access can also be granted to system UIs by specifying {@link * SetSchemaRequest.Builder#setSchemaTypeVisibilityForSystemUi} when building a schema. * - * <p>See {@link AppSearchSession#search} for a detailed explanation on - * forming a query string. + * <p>See {@link AppSearchSession#search} for a detailed explanation on forming a query string. * * <p>This method is lightweight. The heavy work will be done in {@link * SearchResults#getNextPage}. diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java index a63e01555f6c..e9e978eea943 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java +++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java @@ -33,12 +33,16 @@ import java.util.concurrent.Executor; import java.util.function.Consumer; /** - * SearchResults are a returned object from a query API. + * Encapsulates results of a search operation. * - * <p>Each {@link SearchResult} contains a document and may contain other fields like snippets based - * on request. + * <p>Each {@link AppSearchSession#search} operation returns a list of {@link SearchResult} objects, + * referred to as a "page", limited by the size configured by {@link + * SearchSpec.Builder#setResultCountPerPage}. * - * <p>Should close this object after finish fetching results. + * <p>To fetch a page of results, call {@link #getNextPage}. + * + * <p>All instances of {@link SearchResults} must call {@link SearchResults#close()} after the + * results are fetched. * * <p>This class is not thread safe. */ @@ -87,12 +91,12 @@ public class SearchResults implements Closeable { } /** - * Gets a whole page of {@link SearchResult}s. + * Retrieves the next page of {@link SearchResult} objects. * - * <p>Re-call this method to get next page of {@link SearchResult}, until it returns an empty - * list. + * <p>The page size is configured by {@link SearchSpec.Builder#setResultCountPerPage}. * - * <p>The page size is set by {@link SearchSpec.Builder#setResultCountPerPage}. + * <p>Continue calling this method to access results until it returns an empty list, signifying + * there are no more results. * * @param callback Callback to receive the pending result of performing this operation. */ 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 138eb23148af..72bb9f3d07c8 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java @@ -583,9 +583,9 @@ public class GenericDocument { * @param schemaType the {@link AppSearchSchema} type of the {@link GenericDocument}. The * provided {@code schemaType} must be defined using {@link AppSearchSession#setSchema} * prior to inserting a document of this {@code schemaType} into the AppSearch index - * using {@link AppSearchSession#put}. Otherwise, the document will - * be rejected by {@link AppSearchSession#put} with result code - * {@link AppSearchResult#RESULT_NOT_FOUND}. + * using {@link AppSearchSession#put}. Otherwise, the document will be rejected by + * {@link AppSearchSession#put} with result code {@link + * AppSearchResult#RESULT_NOT_FOUND}. */ @SuppressWarnings("unchecked") public Builder(@NonNull String uri, @NonNull String schemaType) { 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 05b212880962..01473be062bc 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java @@ -28,9 +28,9 @@ import java.util.Collections; import java.util.List; /** - * Encapsulates a request to index a document into an {@link AppSearchSession} database. + * Encapsulates a request to index documents into an {@link AppSearchSession} database. * - * <p>@see AppSearchSession#putDocuments + * @see AppSearchSession#put */ public final class PutDocumentsRequest { private final List<GenericDocument> mDocuments; @@ -39,7 +39,7 @@ public final class PutDocumentsRequest { mDocuments = documents; } - /** Returns the documents that are part of this request. */ + /** Returns a list of {@link GenericDocument} objects that are part of this request. */ @NonNull public List<GenericDocument> getGenericDocuments() { return Collections.unmodifiableList(mDocuments); @@ -54,14 +54,22 @@ public final class PutDocumentsRequest { private final List<GenericDocument> mDocuments = new ArrayList<>(); private boolean mBuilt = false; - /** Adds one or more {@link GenericDocument} objects to the request. */ + /** + * Adds one or more {@link GenericDocument} objects to the request. + * + * @throws IllegalStateException if the builder has already been used. + */ @NonNull public Builder addGenericDocuments(@NonNull GenericDocument... documents) { Preconditions.checkNotNull(documents); return addGenericDocuments(Arrays.asList(documents)); } - /** Adds a collection of {@link GenericDocument} objects to the request. */ + /** + * Adds a collection of {@link GenericDocument} objects to the request. + * + * @throws IllegalStateException if the builder has already been used. + */ @NonNull public Builder addGenericDocuments( @NonNull Collection<? extends GenericDocument> documents) { @@ -71,7 +79,11 @@ public final class PutDocumentsRequest { return this; } - /** Creates a new {@link PutDocumentsRequest} object. */ + /** + * Creates a new {@link PutDocumentsRequest} object. + * + * @throws IllegalStateException if the builder has already been used. + */ @NonNull public PutDocumentsRequest build() { Preconditions.checkState(!mBuilt, "Builder has already been used"); 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 2caa94a5ef07..426a903981b3 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java @@ -32,6 +32,41 @@ import java.util.Set; /** * Encapsulates a request to update the schema of an {@link AppSearchSession} database. * + * <p>The schema is composed of a collection of {@link AppSearchSchema} objects, each of which + * defines a unique type of data. + * + * <p>The first call to SetSchemaRequest will set the provided schema and store it within the {@link + * AppSearchSession} database. + * + * <p>Subsequent calls will compare the provided schema to the previously saved schema, to determine + * how to treat existing documents. + * + * <p>The following types of schema modifications are always safe and are made without deleting any + * existing documents: + * + * <ul> + * <li>Addition of new {@link AppSearchSchema} types + * <li>Addition of new properties to an existing {@link AppSearchSchema} type + * <li>Changing the cardinality of a property to be less restrictive + * </ul> + * + * <p>The following types of schema changes are not backwards compatible: + * + * <ul> + * <li>Removal of an existing {@link AppSearchSchema} type + * <li>Removal of a property from an existing {@link AppSearchSchema} type + * <li>Changing the data type of an existing property + * <li>Changing the cardinality of a property to be more restrictive + * </ul> + * + * <p>Providing a schema with incompatible changes, will throw an {@link + * android.app.appsearch.exceptions.AppSearchException}, with a message describing the + * incompatibility. As a result, the previously set schema will remain unchanged. + * + * <p>Backward incompatible changes can be made by setting {@link + * SetSchemaRequest.Builder#setForceOverride} method to {@code true}. This deletes all documents + * that are incompatible with the new schema. The new schema is then saved and persisted to disk. + * * @see AppSearchSession#setSchema */ public final class SetSchemaRequest { @@ -54,14 +89,15 @@ public final class SetSchemaRequest { mForceOverride = forceOverride; } - /** Returns the schemas that are part of this request. */ + /** Returns the {@link AppSearchSchema} types that are part of this request. */ @NonNull public Set<AppSearchSchema> getSchemas() { return Collections.unmodifiableSet(mSchemas); } /** - * Returns the set of schema types that have opted out of being visible on system UI surfaces. + * Returns all the schema types that are opted out of being displayed and visible on any system + * UI surface. */ @NonNull public Set<String> getSchemasNotVisibleToSystemUi() { @@ -70,10 +106,9 @@ public final class SetSchemaRequest { /** * Returns a mapping of schema types to the set of packages that have access to that schema - * type. Each package is represented by a {@link PackageIdentifier}. name and byte[] - * certificate. + * type. * - * <p>This method is inefficient to call repeatedly. + * <p>It’s inefficient to call this method repeatedly. */ @NonNull public Map<String, Set<PackageIdentifier>> getSchemasVisibleToPackages() { @@ -91,9 +126,8 @@ public final class SetSchemaRequest { } /** - * Returns a mapping of schema types to the set of packages that have access to that schema - * type. Each package is represented by a {@link PackageIdentifier}. name and byte[] - * certificate. + * Returns a mapping of {@link AppSearchSchema} types to the set of packages that have access to + * that schema type. * * <p>A more efficient version of {@link #getSchemasVisibleToPackages}, but it returns a * modifiable map. This is not meant to be unhidden and should only be used by internal classes. @@ -110,7 +144,11 @@ public final class SetSchemaRequest { return mForceOverride; } - /** Builder for {@link SetSchemaRequest} objects. */ + /** + * Builder for {@link SetSchemaRequest} objects. + * + * <p>Once {@link #build} is called, the instance can no longer be used. + */ public static final class Builder { private final Set<AppSearchSchema> mSchemas = new ArraySet<>(); private final Set<String> mSchemasNotVisibleToSystemUi = new ArraySet<>(); @@ -121,9 +159,11 @@ public final class SetSchemaRequest { private boolean mBuilt = false; /** - * Adds one or more types to the schema. + * Adds one or more {@link AppSearchSchema} types to the schema. + * + * <p>An {@link AppSearchSchema} object represents one type of structured data. * - * <p>Any documents of these types will be visible on system UI surfaces by default. + * @throws IllegalStateException if the builder has already been used. */ @NonNull public Builder addSchemas(@NonNull AppSearchSchema... schemas) { @@ -132,9 +172,11 @@ public final class SetSchemaRequest { } /** - * Adds one or more types to the schema. + * Adds a collection of {@link AppSearchSchema} objects to the schema. * - * <p>Any documents of these types will be visible on system UI surfaces by default. + * <p>An {@link AppSearchSchema} object represents one type of structured data. + * + * @throws IllegalStateException if the builder has already been used. */ @NonNull public Builder addSchemas(@NonNull Collection<AppSearchSchema> schemas) { @@ -145,10 +187,17 @@ public final class SetSchemaRequest { } /** - * Sets visibility on system UI surfaces for the given {@code schemaType}. + * Sets whether or not documents from the provided {@code schemaType} will be displayed and + * visible on any system UI surface. + * + * <p>This setting applies to the provided {@code schemaType} only, and does not persist + * across {@link AppSearchSession#setSchema} calls. + * + * <p>By default, documents are displayed and visible on system UI surfaces. * * @param schemaType The schema type to set visibility on. * @param visible Whether the {@code schemaType} will be visible or not. + * @throws IllegalStateException if the builder has already been used. */ // Merged list available from getSchemasNotVisibleToSystemUi @SuppressLint("MissingGetterMatchingBuilder") @@ -167,11 +216,25 @@ public final class SetSchemaRequest { } /** - * Sets visibility for a package for the given {@code schemaType}. + * Sets whether or not documents from the provided {@code schemaType} can be read by the + * specified package. + * + * <p>Each package is represented by a {@link PackageIdentifier}, containing a package name + * and a byte array of type {@link android.content.pm.PackageManager#CERT_INPUT_SHA256}. + * + * <p>To opt into one-way data sharing with another application, the developer will need to + * explicitly grant the other application’s package name and certificate Read access to its + * data. + * + * <p>For two-way data sharing, both applications need to explicitly grant Read access to + * one another. + * + * <p>By default, data sharing between applications is disabled. * * @param schemaType The schema type to set visibility on. * @param visible Whether the {@code schemaType} will be visible or not. * @param packageIdentifier Represents the package that will be granted visibility. + * @throws IllegalStateException if the builder has already been used. */ // Merged list available from getSchemasVisibleToPackages @SuppressLint("MissingGetterMatchingBuilder") @@ -224,13 +287,15 @@ public final class SetSchemaRequest { } /** - * Configures the {@link SetSchemaRequest} to delete any existing documents that don't - * follow the new schema. + * Sets whether or not to override the current schema in the {@link AppSearchSession} + * database. * - * <p>By default, this is {@code false} and schema incompatibility causes the {@link - * AppSearchSession#setSchema} call to fail. + * <p>Call this method whenever backward incompatible changes need to be made by setting + * {@code forceOverride} to {@code true}. As a result, during execution of the setSchema + * operation, all documents that are incompatible with the new schema will be deleted and + * the new schema will be saved and persisted. * - * @see AppSearchSession#setSchema + * <p>By default, this is {@code false}. */ @NonNull public Builder setForceOverride(boolean forceOverride) { @@ -239,10 +304,11 @@ public final class SetSchemaRequest { } /** - * Builds a new {@link SetSchemaRequest}. + * Builds a new {@link SetSchemaRequest} object. * - * @throws IllegalArgumentException If schema types were referenced, but the corresponding - * {@link AppSearchSchema} was never added. + * @throws IllegalArgumentException if schema types were referenced, but the corresponding + * {@link AppSearchSchema} type was never added. + * @throws IllegalStateException if the builder has already been used. */ @NonNull public SetSchemaRequest build() { 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 bc99d4f67d86..a146006f355c 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java @@ -79,12 +79,6 @@ public class SetSchemaResponse { return mBundle; } - /** TODO(b/177266929): Remove this deprecated method */ - //@Deprecated - public boolean isSuccess() { - return true; - } - /** * Returns a {@link List} of all failed {@link MigrationFailure}. * diff --git a/apex/appsearch/service/Android.bp b/apex/appsearch/service/Android.bp index 2fd5c733473e..57ee1ec482d8 100644 --- a/apex/appsearch/service/Android.bp +++ b/apex/appsearch/service/Android.bp @@ -11,6 +11,15 @@ // 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + java_library { name: "service-appsearch", installable: true, diff --git a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java index babcd25e3e26..7c92456bea49 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java @@ -218,9 +218,23 @@ public class VisibilityStore { * @throws AppSearchException AppSearchException on AppSearchImpl error. */ public void initialize() throws AppSearchException { - if (!mAppSearchImpl.hasSchemaTypeLocked(PACKAGE_NAME, DATABASE_NAME, VISIBILITY_TYPE) - || !mAppSearchImpl.hasSchemaTypeLocked( - PACKAGE_NAME, DATABASE_NAME, PACKAGE_ACCESSIBLE_TYPE)) { + List<AppSearchSchema> schemas = mAppSearchImpl.getSchema(PACKAGE_NAME, DATABASE_NAME); + boolean hasVisibilityType = false; + boolean hasPackageAccessibleType = false; + for (int i = 0; i < schemas.size(); i++) { + AppSearchSchema schema = schemas.get(i); + if (schema.getSchemaType().equals(VISIBILITY_TYPE)) { + hasVisibilityType = true; + } else if (schema.getSchemaType().equals(PACKAGE_ACCESSIBLE_TYPE)) { + hasPackageAccessibleType = true; + } + + if (hasVisibilityType && hasPackageAccessibleType) { + // Found both our types, can exit early. + break; + } + } + if (!hasVisibilityType || !hasPackageAccessibleType) { // Schema type doesn't exist yet. Add it. mAppSearchImpl.setSchema( PACKAGE_NAME, @@ -250,10 +264,11 @@ public class VisibilityStore { /*typePropertyPaths=*/ Collections.emptyMap()); // Update platform visibility settings - String[] schemas = + String[] notPlatformSurfaceableSchemas = document.getPropertyStringArray(NOT_PLATFORM_SURFACEABLE_PROPERTY); - if (schemas != null) { - mNotPlatformSurfaceableMap.put(prefix, new ArraySet<>(Arrays.asList(schemas))); + if (notPlatformSurfaceableSchemas != null) { + mNotPlatformSurfaceableMap.put( + prefix, new ArraySet<>(Arrays.asList(notPlatformSurfaceableSchemas))); } // Update 3p package visibility settings @@ -333,7 +348,7 @@ public class VisibilityStore { schemasPackageAccessible.entrySet()) { for (int i = 0; i < entry.getValue().size(); i++) { GenericDocument packageAccessibleDocument = - new GenericDocument.Builder(/*uri=*/"", PACKAGE_ACCESSIBLE_TYPE) + new GenericDocument.Builder(/*uri=*/ "", PACKAGE_ACCESSIBLE_TYPE) .setNamespace(NAMESPACE) .setPropertyString( PACKAGE_NAME_PROPERTY, 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 6c2e30e98877..e2c211b7d303 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 @@ -654,6 +654,35 @@ public final class AppSearchImpl implements Closeable { } } + /** + * Returns a mapping of package names to all the databases owned by that package. + * + * <p>This method is inefficient to call repeatedly. + */ + @NonNull + public Map<String, Set<String>> getPackageToDatabases() { + mReadWriteLock.readLock().lock(); + try { + Map<String, Set<String>> packageToDatabases = new ArrayMap<>(); + for (String prefix : mSchemaMapLocked.keySet()) { + String packageName = getPackageName(prefix); + + Set<String> databases = packageToDatabases.get(packageName); + if (databases == null) { + databases = new ArraySet<>(); + packageToDatabases.put(packageName, databases); + } + + String databaseName = getDatabaseName(prefix); + databases.add(databaseName); + } + + return packageToDatabases; + } finally { + mReadWriteLock.readLock().unlock(); + } + } + @GuardedBy("mReadWriteLock") private SearchResultPage doQueryLocked( @NonNull Set<String> prefixes, @@ -1211,26 +1240,8 @@ public final class AppSearchImpl implements Closeable { return schemaProto.getSchema(); } - /** - * Returns true if the {@code packageName} and {@code databaseName} has the {@code schemaType} - */ - @GuardedBy("mReadWriteLock") - boolean hasSchemaTypeLocked( - @NonNull String packageName, @NonNull String databaseName, @NonNull String schemaType) { - Preconditions.checkNotNull(packageName); - Preconditions.checkNotNull(databaseName); - Preconditions.checkNotNull(schemaType); - - String prefix = createPrefix(packageName, databaseName); - Set<String> schemaTypes = mSchemaMapLocked.get(prefix); - if (schemaTypes == null) { - return false; - } - - return schemaTypes.contains(prefix + schemaType); - } - /** Returns a set of all prefixes AppSearchImpl knows about. */ + // TODO(b/180058203): Remove this method once platform has switched away from using this method. @GuardedBy("mReadWriteLock") @NonNull Set<String> getPrefixesLocked() { diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java new file mode 100644 index 000000000000..0f23d926648b --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java @@ -0,0 +1,43 @@ +/* + * 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 com.android.server.appsearch.external.localstorage; + +import android.annotation.NonNull; +import android.app.appsearch.exceptions.AppSearchException; + +import com.android.server.appsearch.external.localstorage.stats.CallStats; +import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats; + +/** + * An interface for implementing client-defined logging AppSearch operations stats. + * + * <p>Any implementation needs to provide general information on how to log all the stats types. + * (e.g. {@link CallStats}) + * + * <p>All implementations of this interface must be thread safe. + * + * @hide + */ +public interface AppSearchLogger { + /** Logs {@link CallStats} */ + void logStats(@NonNull CallStats stats) throws AppSearchException; + + /** Logs {@link PutDocumentStats} */ + void logStats(@NonNull PutDocumentStats stats) throws AppSearchException; + + // TODO(b/173532925) Add remaining logStats once we add all the stats. +} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java index a501e99db1ef..a7f1cc4c793f 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java @@ -76,7 +76,7 @@ class AppSearchMigrationHelperImpl implements AppSearchMigrationHelper { int currentVersion = mCurrentVersionMap.get(schemaType); int finalVersion = mFinalVersionMap.get(schemaType); try (FileOutputStream outputStream = new FileOutputStream(mFile)) { - // TODO(b/177266929) change the output stream so that we can use it in platform + // TODO(b/151178558) change the output stream so that we can use it in platform CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(outputStream); SearchResultPage searchResultPage = mAppSearchImpl.query( diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java new file mode 100644 index 000000000000..81a5067c9fa1 --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java @@ -0,0 +1,200 @@ +/* + * 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.stats; + +import android.annotation.IntDef; +import android.annotation.NonNull; + +import com.android.internal.util.Preconditions; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * A class for setting basic information to log for all function calls. + * + * <p>This class can set which stats to log for both batch and non-batch {@link + * android.app.appsearch.AppSearchSession} calls. + * + * <p>Some function calls like {@link android.app.appsearch.AppSearchSession#setSchema} have their + * own detailed stats class {@link placeholder}. However, {@link CallStats} can still be used along + * with the detailed stats class for easy aggregation/analysis with other function calls. + * + * @hide + */ +public class CallStats { + @IntDef( + value = { + CALL_TYPE_UNKNOWN, + CALL_TYPE_INITIALIZE, + CALL_TYPE_SET_SCHEMA, + CALL_TYPE_PUT_DOCUMENTS, + CALL_TYPE_GET_DOCUMENTS, + CALL_TYPE_REMOVE_DOCUMENTS, + CALL_TYPE_PUT_DOCUMENT, + CALL_TYPE_GET_DOCUMENT, + CALL_TYPE_REMOVE_DOCUMENT, + CALL_TYPE_QUERY, + CALL_TYPE_OPTIMIZE, + CALL_TYPE_FLUSH, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CallType {} + + public static final int CALL_TYPE_UNKNOWN = 0; + public static final int CALL_TYPE_INITIALIZE = 1; + public static final int CALL_TYPE_SET_SCHEMA = 2; + public static final int CALL_TYPE_PUT_DOCUMENTS = 3; + public static final int CALL_TYPE_GET_DOCUMENTS = 4; + public static final int CALL_TYPE_REMOVE_DOCUMENTS = 5; + public static final int CALL_TYPE_PUT_DOCUMENT = 6; + public static final int CALL_TYPE_GET_DOCUMENT = 7; + public static final int CALL_TYPE_REMOVE_DOCUMENT = 8; + public static final int CALL_TYPE_QUERY = 9; + public static final int CALL_TYPE_OPTIMIZE = 10; + public static final int CALL_TYPE_FLUSH = 11; + + @NonNull private final GeneralStats mGeneralStats; + @CallType private final int mCallType; + private final int mEstimatedBinderLatencyMillis; + private final int mNumOperationsSucceeded; + private final int mNumOperationsFailed; + + CallStats(@NonNull Builder builder) { + Preconditions.checkNotNull(builder); + mGeneralStats = Preconditions.checkNotNull(builder.mGeneralStats); + mCallType = builder.mCallType; + mEstimatedBinderLatencyMillis = builder.mEstimatedBinderLatencyMillis; + mNumOperationsSucceeded = builder.mNumOperationsSucceeded; + mNumOperationsFailed = builder.mNumOperationsFailed; + } + + /** Returns general information for the call. */ + @NonNull + public GeneralStats getGeneralStats() { + return mGeneralStats; + } + + /** Returns type of the call. */ + @CallType + public int getCallType() { + return mCallType; + } + + /** Returns estimated binder latency, in milliseconds */ + public int getEstimatedBinderLatencyMillis() { + return mEstimatedBinderLatencyMillis; + } + + /** + * Returns number of operations succeeded. + * + * <p>For example, for {@link android.app.appsearch.AppSearchSession#put}, it is the total + * number of individual successful put operations. In this case, how many documents are + * successfully indexed. + * + * <p>For non-batch calls such as {@link android.app.appsearch.AppSearchSession#setSchema}, the + * sum of {@link CallStats#getNumOperationsSucceeded()} and {@link + * CallStats#getNumOperationsFailed()} is always 1 since there is only one operation. + */ + public int getNumOperationsSucceeded() { + return mNumOperationsSucceeded; + } + + /** + * Returns number of operations failed. + * + * <p>For example, for {@link android.app.appsearch.AppSearchSession#put}, it is the total + * number of individual failed put operations. In this case, how many documents are failed to be + * indexed. + * + * <p>For non-batch calls such as {@link android.app.appsearch.AppSearchSession#setSchema}, the + * sum of {@link CallStats#getNumOperationsSucceeded()} and {@link + * CallStats#getNumOperationsFailed()} is always 1 since there is only one operation. + */ + public int getNumOperationsFailed() { + return mNumOperationsFailed; + } + + /** Builder for {@link CallStats}. */ + public static class Builder { + @NonNull final GeneralStats mGeneralStats; + @CallType int mCallType; + int mEstimatedBinderLatencyMillis; + int mNumOperationsSucceeded; + int mNumOperationsFailed; + + /** Builder takes {@link GeneralStats} to hold general stats. */ + public Builder(@NonNull GeneralStats generalStats) { + mGeneralStats = Preconditions.checkNotNull(generalStats); + } + + /** Sets type of the call. */ + @NonNull + public Builder setCallType(@CallType int callType) { + mCallType = callType; + return this; + } + + /** Sets estimated binder latency, in milliseconds. */ + @NonNull + public Builder setEstimatedBinderLatencyMillis(int estimatedBinderLatencyMillis) { + mEstimatedBinderLatencyMillis = estimatedBinderLatencyMillis; + return this; + } + + /** + * Sets number of operations succeeded. + * + * <p>For example, for {@link android.app.appsearch.AppSearchSession#put}, it is the total + * number of individual successful put operations. In this case, how many documents are + * successfully indexed. + * + * <p>For non-batch calls such as {@link android.app.appsearch.AppSearchSession#setSchema}, + * the sum of {@link CallStats#getNumOperationsSucceeded()} and {@link + * CallStats#getNumOperationsFailed()} is always 1 since there is only one operation. + */ + @NonNull + public Builder setNumOperationsSucceeded(int numOperationsSucceeded) { + mNumOperationsSucceeded = numOperationsSucceeded; + return this; + } + + /** + * Sets number of operations failed. + * + * <p>For example, for {@link android.app.appsearch.AppSearchSession#put}, it is the total + * number of individual failed put operations. In this case, how many documents are failed + * to be indexed. + * + * <p>For non-batch calls such as {@link android.app.appsearch.AppSearchSession#setSchema}, + * the sum of {@link CallStats#getNumOperationsSucceeded()} and {@link + * CallStats#getNumOperationsFailed()} is always 1 since there is only one operation. + */ + @NonNull + public Builder setNumOperationsFailed(int numOperationsFailed) { + mNumOperationsFailed = numOperationsFailed; + return this; + } + + /** Creates {@link CallStats} object from {@link Builder} instance. */ + @NonNull + public CallStats build() { + return new CallStats(/* builder= */ this); + } + } +} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java new file mode 100644 index 000000000000..d2a45d5304f9 --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java @@ -0,0 +1,122 @@ +/* + * 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.stats; + +import android.annotation.NonNull; +import android.app.appsearch.AppSearchResult; + +import com.android.internal.util.Preconditions; + +/** + * A class for holding general logging information. + * + * <p>This class cannot be logged by {@link + * com.android.server.appsearch.external.localstorage.AppSearchLogger} directly. It is used for + * defining general logging information that is shared across different stats classes. + * + * @see PutDocumentStats + * @see CallStats + * @hide + */ +public final class GeneralStats { + /** Package name of the application. */ + @NonNull private final String mPackageName; + + /** Database name within AppSearch. */ + @NonNull private final String mDatabase; + + /** + * The status code returned by {@link AppSearchResult#getResultCode()} for the call or internal + * state. + */ + @AppSearchResult.ResultCode private final int mStatusCode; + + private final int mTotalLatencyMillis; + + GeneralStats(@NonNull Builder builder) { + Preconditions.checkNotNull(builder); + mPackageName = Preconditions.checkNotNull(builder.mPackageName); + mDatabase = Preconditions.checkNotNull(builder.mDatabase); + mStatusCode = builder.mStatusCode; + mTotalLatencyMillis = builder.mTotalLatencyMillis; + } + + /** Returns package name. */ + @NonNull + public String getPackageName() { + return mPackageName; + } + + /** Returns database name. */ + @NonNull + public String getDatabase() { + return mDatabase; + } + + /** Returns result code from {@link AppSearchResult#getResultCode()} */ + @AppSearchResult.ResultCode + public int getStatusCode() { + return mStatusCode; + } + + /** Returns total latency, in milliseconds. */ + public int getTotalLatencyMillis() { + return mTotalLatencyMillis; + } + + /** Builder for {@link GeneralStats}. */ + public static class Builder { + @NonNull final String mPackageName; + @NonNull final String mDatabase; + @AppSearchResult.ResultCode int mStatusCode; + int mTotalLatencyMillis; + + /** + * Constructor + * + * @param packageName name of the package logging stats + * @param dataBase name of the database logging stats + */ + public Builder(@NonNull String packageName, @NonNull String dataBase) { + mPackageName = Preconditions.checkNotNull(packageName); + mDatabase = Preconditions.checkNotNull(dataBase); + } + + /** Sets status code returned from {@link AppSearchResult#getResultCode()} */ + @NonNull + public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) { + mStatusCode = statusCode; + return this; + } + + /** Sets total latency, in milliseconds. */ + @NonNull + public Builder setTotalLatencyMillis(int totalLatencyMillis) { + mTotalLatencyMillis = totalLatencyMillis; + return this; + } + + /** + * Creates a new {@link GeneralStats} object from the contents of this {@link Builder} + * instance. + */ + @NonNull + public GeneralStats build() { + return new GeneralStats(/* builder= */ this); + } + } +} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java new file mode 100644 index 000000000000..b1b643b66859 --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java @@ -0,0 +1,219 @@ +/* + * 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.stats; + +import android.annotation.NonNull; + +import com.android.internal.util.Preconditions; + +/** + * A class for holding detailed stats to log for each individual document put by a {@link + * android.app.appsearch.AppSearchSession#put} call. + * + * @hide + */ +public final class PutDocumentStats { + /** {@link GeneralStats} holds the general stats. */ + @NonNull private final GeneralStats mGeneralStats; + + /** Time used to generate a document proto from a Bundle. */ + private final int mGenerateDocumentProtoLatencyMillis; + + /** Time used to rewrite types and namespaces in the document. */ + private final int mRewriteDocumentTypesLatencyMillis; + + /** Overall time used for the native function call. */ + private final int mNativeLatencyMillis; + + /** Time used to store the document. */ + private final int mNativeDocumentStoreLatencyMillis; + + /** Time used to index the document. It doesn't include the time to merge indices. */ + private final int mNativeIndexLatencyMillis; + + /** Time used to merge the indices. */ + private final int mNativeIndexMergeLatencyMillis; + + /** Document size in bytes. */ + private final int mNativeDocumentSizeBytes; + + /** Number of tokens added to the index. */ + private final int mNativeNumTokensIndexed; + + /** Number of tokens clipped for exceeding the max number. */ + private final int mNativeNumTokensClipped; + + PutDocumentStats(@NonNull Builder builder) { + Preconditions.checkNotNull(builder); + mGeneralStats = Preconditions.checkNotNull(builder.mGeneralStats); + mGenerateDocumentProtoLatencyMillis = builder.mGenerateDocumentProtoLatencyMillis; + mRewriteDocumentTypesLatencyMillis = builder.mRewriteDocumentTypesLatencyMillis; + mNativeLatencyMillis = builder.mNativeLatencyMillis; + mNativeDocumentStoreLatencyMillis = builder.mNativeDocumentStoreLatencyMillis; + mNativeIndexLatencyMillis = builder.mNativeIndexLatencyMillis; + mNativeIndexMergeLatencyMillis = builder.mNativeIndexMergeLatencyMillis; + mNativeDocumentSizeBytes = builder.mNativeDocumentSizeBytes; + mNativeNumTokensIndexed = builder.mNativeNumTokensIndexed; + mNativeNumTokensClipped = builder.mNativeNumTokensClipped; + } + + /** Returns the {@link GeneralStats} object attached to this instance. */ + @NonNull + public GeneralStats getGeneralStats() { + return mGeneralStats; + } + + /** Returns time spent on generating document proto, in milliseconds. */ + public int getGenerateDocumentProtoLatencyMillis() { + return mGenerateDocumentProtoLatencyMillis; + } + + /** Returns time spent on rewriting types and namespaces in document, in milliseconds. */ + public int getRewriteDocumentTypesLatencyMillis() { + return mRewriteDocumentTypesLatencyMillis; + } + + /** Returns time spent in native, in milliseconds. */ + public int getNativeLatencyMillis() { + return mNativeLatencyMillis; + } + + /** Returns time spent on document store, in milliseconds. */ + public int getNativeDocumentStoreLatencyMillis() { + return mNativeDocumentStoreLatencyMillis; + } + + /** Returns time spent on indexing, in milliseconds. */ + public int getNativeIndexLatencyMillis() { + return mNativeIndexLatencyMillis; + } + + /** Returns time spent on merging indices, in milliseconds. */ + public int getNativeIndexMergeLatencyMillis() { + return mNativeIndexMergeLatencyMillis; + } + + /** Returns document size, in bytes. */ + public int getNativeDocumentSizeBytes() { + return mNativeDocumentSizeBytes; + } + + /** Returns number of tokens indexed. */ + public int getNativeNumTokensIndexed() { + return mNativeNumTokensIndexed; + } + + /** Returns number of tokens clipped for exceeding the max number. */ + public int getNativeNumTokensClipped() { + return mNativeNumTokensClipped; + } + + /** Builder for {@link PutDocumentStats}. */ + public static class Builder { + @NonNull final GeneralStats mGeneralStats; + int mGenerateDocumentProtoLatencyMillis; + int mRewriteDocumentTypesLatencyMillis; + int mNativeLatencyMillis; + int mNativeDocumentStoreLatencyMillis; + int mNativeIndexLatencyMillis; + int mNativeIndexMergeLatencyMillis; + int mNativeDocumentSizeBytes; + int mNativeNumTokensIndexed; + int mNativeNumTokensClipped; + + /** Builder takes {@link GeneralStats} to hold general stats. */ + public Builder(@NonNull GeneralStats generalStats) { + mGeneralStats = Preconditions.checkNotNull(generalStats); + } + + /** Sets how much time we spend for generating document proto, in milliseconds. */ + @NonNull + public Builder setGenerateDocumentProtoLatencyMillis( + int generateDocumentProtoLatencyMillis) { + mGenerateDocumentProtoLatencyMillis = generateDocumentProtoLatencyMillis; + return this; + } + + /** + * Sets how much time we spend for rewriting types and namespaces in document, in + * milliseconds. + */ + @NonNull + public Builder setRewriteDocumentTypesLatencyMillis(int rewriteDocumentTypesLatencyMillis) { + mRewriteDocumentTypesLatencyMillis = rewriteDocumentTypesLatencyMillis; + return this; + } + + /** Sets the native latency, in milliseconds. */ + @NonNull + public Builder setNativeLatencyMillis(int nativeLatencyMillis) { + mNativeLatencyMillis = nativeLatencyMillis; + return this; + } + + /** Sets how much time we spend on document store, in milliseconds. */ + @NonNull + public Builder setNativeDocumentStoreLatencyMillis(int nativeDocumentStoreLatencyMillis) { + mNativeDocumentStoreLatencyMillis = nativeDocumentStoreLatencyMillis; + return this; + } + + /** Sets the native index latency, in milliseconds. */ + @NonNull + public Builder setNativeIndexLatencyMillis(int nativeIndexLatencyMillis) { + mNativeIndexLatencyMillis = nativeIndexLatencyMillis; + return this; + } + + /** Sets how much time we spend on merging indices, in milliseconds. */ + @NonNull + public Builder setNativeIndexMergeLatencyMillis(int nativeIndexMergeLatencyMillis) { + mNativeIndexMergeLatencyMillis = nativeIndexMergeLatencyMillis; + return this; + } + + /** Sets document size, in bytes. */ + @NonNull + public Builder setNativeDocumentSizeBytes(int nativeDocumentSizeBytes) { + mNativeDocumentSizeBytes = nativeDocumentSizeBytes; + return this; + } + + /** Sets number of tokens indexed in native. */ + @NonNull + public Builder setNativeNumTokensIndexed(int nativeNumTokensIndexed) { + mNativeNumTokensIndexed = nativeNumTokensIndexed; + return this; + } + + /** Sets number of tokens clipped for exceeding the max number. */ + @NonNull + public Builder setNativeNumTokensClipped(int nativeNumTokensClipped) { + mNativeNumTokensClipped = nativeNumTokensClipped; + return this; + } + + /** + * Creates a new {@link PutDocumentStats} object from the contents of this {@link Builder} + * instance. + */ + @NonNull + public PutDocumentStats build() { + return new PutDocumentStats(/* builder= */ this); + } + } +} diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt index d076db3b8f82..2c9477a5d76b 100644 --- a/apex/appsearch/synced_jetpack_changeid.txt +++ b/apex/appsearch/synced_jetpack_changeid.txt @@ -1 +1 @@ -Ia9a8daef1a6d7d9432f7808d440abd64f4797701 +I593dfd22279739e5f578e07d36a55cf02ee942c5 diff --git a/apex/appsearch/testing/Android.bp b/apex/appsearch/testing/Android.bp index 54d50395e3bd..eb072afec696 100644 --- a/apex/appsearch/testing/Android.bp +++ b/apex/appsearch/testing/Android.bp @@ -11,6 +11,15 @@ // 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + java_library { name: "AppSearchTestUtils", srcs: ["java/**/*.java"], 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 ea21e19b2bea..1428fb1d3c7a 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,91 +33,19 @@ import java.util.Set; public interface AppSearchSessionShim extends Closeable { /** - * Sets the schema that will be used by documents provided to the {@link #put} method. + * Sets the schema that represents the organizational structure of data within the AppSearch + * database. * - * <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 - * types of schema modifications are always safe and are made without deleting any existing - * documents: + * <p>Upon creating an {@link AppSearchSessionShim}, {@link #setSchema} should be called. If the + * schema needs to be updated, or it has not been previously set, then the provided schema will + * be saved and persisted to disk. Otherwise, {@link #setSchema} is handled efficiently as a + * no-op call. * - * <ul> - * <li>Addition of new types - * <li>Addition of new {@link AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} or - * {@link AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED} properties to a - * type - * <li>Changing the cardinality of a data type to be less restrictive (e.g. changing an {@link - * AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a {@link - * AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED} property. - * </ul> - * - * <p>The following types of schema changes are not backwards-compatible: - * - * <ul> - * <li>Removal of an existing type - * <li>Removal of a property from a type - * <li>Changing the data type ({@code boolean}, {@code long}, etc.) of an existing property - * <li>For properties of {@code Document} type, changing the schema type of {@code Document}s - * of that property - * <li>Changing the cardinality of a data type to be more restrictive (e.g. changing an {@link - * AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a {@link - * AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property). - * <li>Adding a {@link AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property. - * </ul> - * - * <p>Supplying a schema with such changes will, by default, result in this call completing its - * future with an {@link android.app.appsearch.exceptions.AppSearchException} with a code of - * {@link AppSearchResult#RESULT_INVALID_SCHEMA} and a message describing the incompatibility. - * In this case the previously set schema will remain active. - * - * <p>If you need to make non-backwards-compatible changes as described above, you can either: - * - * <ul> - * <li>Set the {@link SetSchemaRequest.Builder#setForceOverride} method to {@code true}. In - * this case, instead of completing its future with an {@link - * android.app.appsearch.exceptions.AppSearchException} with the {@link - * AppSearchResult#RESULT_INVALID_SCHEMA} error code, all documents which are not - * compatible with the new schema will be deleted and the incompatible schema will be - * applied. Incompatible types and deleted types will be set into {@link - * SetSchemaResponse#getIncompatibleTypes()} and {@link - * SetSchemaResponse#getDeletedTypes()}, respectively. - * <li>Add a {@link android.app.appsearch.AppSearchSchema.Migrator} for each incompatible type - * and make no deletion. The migrator will migrate documents from it's old schema version - * to the new version. Migrated types will be set into both {@link - * SetSchemaResponse#getIncompatibleTypes()} and {@link - * SetSchemaResponse#getMigratedTypes()}. See the migration section below. - * </ul> - * - * <p>It is a no-op to set the same schema as has been previously set; this is handled - * efficiently. - * - * <p>By default, documents are visible on platform surfaces. To opt out, call - * {@link SetSchemaRequest.Builder#setSchemaTypeVisibilityForSystemUi} with {@code visible} as - * false. Any visibility settings apply only to the schemas that are included in the - * {@code request}. Visibility settings for a schema type do not persist across - * {@link #setSchema} calls. - * - * <p>Migration: make non-backwards-compatible changes will delete all stored documents in old - * schema. You can save your documents by setting {@link - * android.app.appsearch.AppSearchSchema.Migrator} via the {@link - * SetSchemaRequest.Builder#setMigrator} for each type you want to save. - * - * <p>{@link android.app.appsearch.AppSearchSchema.Migrator#onDowngrade} or {@link - * android.app.appsearch.AppSearchSchema.Migrator#onUpgrade} will be triggered if the version - * number of the schema stored in AppSearch is different with the version in the request. - * - * <p>If any error or Exception occurred in the {@link - * android.app.appsearch.AppSearchSchema.Migrator#onDowngrade}, {@link - * android.app.appsearch.AppSearchSchema.Migrator#onUpgrade} or {@link - * android.app.appsearch.AppSearchMigrationHelper.Transformer#transform}, the migration will be - * terminated, the setSchema request will be rejected unless the schema changes are - * backwards-compatible, and stored documents won't have any observable changes. - * - * @param request The schema update request. - * @return A {@link ListenableFuture} with exception if we hit any error. Or the pending {@link - * SetSchemaResponse} of performing this operation, if the schema has been successfully set. - * @see android.app.appsearch.AppSearchSchema.Migrator - * @see android.app.appsearch.AppSearchMigrationHelper.Transformer + * @param request the schema to set or update the AppSearch database to. + * @return a {@link ListenableFuture} which resolves to a {@link SetSchemaResponse} object. */ + // TODO(b/169883602): Change @code references to @link when setPlatformSurfaceable APIs are + // exposed. @NonNull ListenableFuture<SetSchemaResponse> setSchema(@NonNull SetSchemaRequest request); @@ -132,15 +60,17 @@ public interface AppSearchSessionShim extends Closeable { ListenableFuture<Set<AppSearchSchema>> getSchema(); /** - * Indexes documents into AppSearch. + * Indexes documents into the {@link AppSearchSessionShim} database. * - * <p>Each {@link GenericDocument}'s {@code schemaType} field must be set to the name of a - * schema type previously registered via the {@link #setSchema} method. + * <p>Each {@link GenericDocument} object must have a {@code schemaType} field set to an {@link + * AppSearchSchema} type that has been previously registered by calling the {@link #setSchema} + * method. * - * @param request {@link PutDocumentsRequest} containing documents to be indexed - * @return The pending result of performing this operation. The keys of the returned {@link - * AppSearchBatchResult} are the URIs of the input documents. The values are {@code null} if - * they were successfully indexed, or a failed {@link AppSearchResult} otherwise. + * @param request containing documents to be indexed. + * @return a {@link ListenableFuture} which resolves to an {@link AppSearchBatchResult}. The + * keys of the returned {@link AppSearchBatchResult} are the URIs of the input documents. + * The values are either {@code null} if the corresponding document was successfully + * indexed, or a failed {@link AppSearchResult} otherwise. */ @NonNull ListenableFuture<AppSearchBatchResult<String, Void>> put(@NonNull PutDocumentsRequest request); @@ -213,7 +143,7 @@ public interface AppSearchSessionShim extends Closeable { * adding projection, can be set by calling the corresponding {@link SearchSpec.Builder} setter. * * <p>This method is lightweight. The heavy work will be done in {@link - * SearchResultsShim#getNextPage()}. + * SearchResultsShim#getNextPage}. * * @param queryExpression query string to search. * @param searchSpec spec for setting document filters, adding projection, setting term match 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 31c934f8bb27..d912c08e7d5f 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 @@ -37,11 +37,11 @@ public interface GlobalSearchSessionShim extends Closeable { * SetSchemaRequest.Builder#setSchemaTypeVisibilityForSystemUi}, or {@link * SetSchemaRequest.Builder#setDocumentClassVisibilityForSystemUi} when building a schema. * - * <p>See {@link AppSearchSessionShim#search(String, SearchSpec)} for a detailed explanation on - * forming a query string. + * <p>See {@link AppSearchSessionShim#search} for a detailed explanation on forming a query + * string. * * <p>This method is lightweight. The heavy work will be done in {@link - * SearchResultsShim#getNextPage()}. + * SearchResultsShim#getNextPage}. * * @param queryExpression query string to search. * @param searchSpec spec for setting document filters, adding projection, setting term match diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/SearchResultsShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/SearchResultsShim.java index 328c65ca2727..38f61f83d24e 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/SearchResultsShim.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/SearchResultsShim.java @@ -24,25 +24,29 @@ import java.io.Closeable; import java.util.List; /** - * SearchResultsShim are a returned object from a query API. + * Encapsulates results of a search operation. * - * <p>Each {@link SearchResult} contains a document and may contain other fields like snippets based - * on request. + * <p>Each {@link AppSearchSessionShim#search} operation returns a list of {@link SearchResult} + * objects, referred to as a "page", limited by the size configured by {@link + * SearchSpec.Builder#setResultCountPerPage}. * - * <p>Should close this object after finish fetching results. + * <p>To fetch a page of results, call {@link #getNextPage()}. + * + * <p>All instances of {@link SearchResultsShim} must call {@link SearchResultsShim#close()} after + * the results are fetched. * * <p>This class is not thread safe. */ public interface SearchResultsShim extends Closeable { /** - * Gets a whole page of {@link SearchResult}s. + * Retrieves the next page of {@link SearchResult} objects. * - * <p>Re-call this method to get next page of {@link SearchResult}, until it returns an empty - * list. + * <p>The page size is configured by {@link SearchSpec.Builder#setResultCountPerPage}. * - * <p>The page size is set by {@link SearchSpec.Builder#setResultCountPerPage}. + * <p>Continue calling this method to access results until it returns an empty list, signifying + * there are no more results. * - * @return The pending result of performing this operation. + * @return a {@link ListenableFuture} which resolves to a list of {@link SearchResult} objects. */ @NonNull ListenableFuture<List<SearchResult>> getNextPage(); diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java index 7c7b21001c3b..77146e0d1282 100644 --- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java +++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java @@ -23,7 +23,7 @@ import android.annotation.SdkConstant; import android.annotation.SystemApi; import android.annotation.SystemService; import android.compat.annotation.ChangeId; -import android.compat.annotation.Disabled; +import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; @@ -202,7 +202,7 @@ public class AlarmManager { * @hide */ @ChangeId - @Disabled // TODO (b/171306433): Enable starting S. + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) public static final long REQUIRE_EXACT_ALARM_PERMISSION = 171306433L; @UnsupportedAppUsage diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index 559a43491c7f..ea733696e1f7 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -423,6 +423,8 @@ public class AlarmManagerService extends SystemService { static final String KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA = "allow_while_idle_compat_quota"; private static final String KEY_ALLOW_WHILE_IDLE_WINDOW = "allow_while_idle_window"; + private static final String KEY_CRASH_NON_CLOCK_APPS = "crash_non_clock_apps"; + private static final long DEFAULT_MIN_FUTURITY = 5 * 1000; private static final long DEFAULT_MIN_INTERVAL = 60 * 1000; private static final long DEFAULT_MAX_INTERVAL = 365 * DateUtils.DAY_IN_MILLIS; @@ -454,6 +456,8 @@ public class AlarmManagerService extends SystemService { private static final int DEFAULT_ALLOW_WHILE_IDLE_QUOTA = 72; private static final long DEFAULT_ALLOW_WHILE_IDLE_WINDOW = 60 * 60 * 1000; // 1 hour. + // TODO (b/171306433): Change to true by default. + private static final boolean DEFAULT_CRASH_NON_CLOCK_APPS = false; // Minimum futurity of a new alarm public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY; @@ -495,6 +499,13 @@ public class AlarmManagerService extends SystemService { */ public long ALLOW_WHILE_IDLE_WINDOW = DEFAULT_ALLOW_WHILE_IDLE_WINDOW; + /** + * Whether or not to crash callers that use setExactAndAllowWhileIdle or setAlarmClock + * but don't hold the required permission. This is useful to catch broken + * apps and reverting to a softer failure in case of broken apps. + */ + public boolean CRASH_NON_CLOCK_APPS = DEFAULT_CRASH_NON_CLOCK_APPS; + private long mLastAllowWhileIdleWhitelistDuration = -1; Constants() { @@ -607,6 +618,10 @@ public class AlarmManagerService extends SystemService { KEY_TIME_TICK_ALLOWED_WHILE_IDLE, DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE); break; + case KEY_CRASH_NON_CLOCK_APPS: + CRASH_NON_CLOCK_APPS = properties.getBoolean(KEY_CRASH_NON_CLOCK_APPS, + DEFAULT_CRASH_NON_CLOCK_APPS); + break; default: if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) { // The quotas need to be updated in order, so we can't just rely @@ -741,6 +756,9 @@ public class AlarmManagerService extends SystemService { pw.print(KEY_TIME_TICK_ALLOWED_WHILE_IDLE, TIME_TICK_ALLOWED_WHILE_IDLE); pw.println(); + pw.print(KEY_CRASH_NON_CLOCK_APPS, CRASH_NON_CLOCK_APPS); + pw.println(); + pw.decreaseIndent(); } @@ -1914,11 +1932,12 @@ public class AlarmManagerService extends SystemService { } /** - * Returns true if the given uid is on the system or user's power save exclusion list. + * Returns true if the given uid does not require SCHEDULE_EXACT_ALARM to set exact, + * allow-while-idle alarms. */ - boolean isWhitelisted(int uid) { - return (mLocalDeviceIdleController == null || mLocalDeviceIdleController.isAppOnWhitelist( - UserHandle.getAppId(uid))); + boolean isExemptFromPermission(int uid) { + return (UserHandle.isSameApp(mSystemUiUid, uid) || mLocalDeviceIdleController == null + || mLocalDeviceIdleController.isAppOnWhitelist(UserHandle.getAppId(uid))); } /** @@ -1949,7 +1968,7 @@ public class AlarmManagerService extends SystemService { if (windowLength != AlarmManager.WINDOW_EXACT) { needsPermission = false; lowQuota = true; - idleOptions = isWhitelisted(callingUid) ? mOptsWithFgs.toBundle() + idleOptions = isExemptFromPermission(callingUid) ? mOptsWithFgs.toBundle() : mOptsWithoutFgs.toBundle(); } else if (alarmClock != null) { needsPermission = true; @@ -1966,16 +1985,22 @@ public class AlarmManagerService extends SystemService { idleOptions = allowWhileIdle ? mOptsWithFgs.toBundle() : null; } if (needsPermission && !canScheduleExactAlarms()) { - if (alarmClock == null && isWhitelisted(callingUid)) { + if (alarmClock == null && isExemptFromPermission(callingUid)) { // If the app is on the full system allow-list (not except-idle), we still // allow the alarms, but with a lower quota to keep pre-S compatibility. lowQuota = true; } else { - final String errorMessage = "Caller needs to hold " + final String errorMessage = "Caller " + callingPackage + " needs to hold " + Manifest.permission.SCHEDULE_EXACT_ALARM + " to set " + ((allowWhileIdle) ? "exact, allow-while-idle" : "alarm-clock") + " alarms."; - throw new SecurityException(errorMessage); + if (mConstants.CRASH_NON_CLOCK_APPS) { + throw new SecurityException(errorMessage); + } else { + Slog.wtf(TAG, errorMessage); + idleOptions = mOptsWithoutFgs.toBundle(); + lowQuota = allowWhileIdle; + } } } if (lowQuota) { @@ -2933,7 +2958,7 @@ public class AlarmManagerService extends SystemService { if (UserHandle.isCore(uid) || uid == mSystemUiUid) { return; } - if (isWhitelisted(uid)) { + if (isExemptFromPermission(uid)) { return; } if (!CompatChanges.isChangeEnabled( diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index c5d3b7a726b9..fc2a409be2c2 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -53,7 +53,6 @@ import android.net.Uri; import android.os.BatteryStats; import android.os.BatteryStatsInternal; import android.os.Binder; -import android.os.Build; import android.os.Handler; import android.os.LimitExceededException; import android.os.Looper; @@ -153,8 +152,7 @@ public class JobSchedulerService extends com.android.server.SystemService /** The maximum number of jobs that we allow an unprivileged app to schedule */ private static final int MAX_JOBS_PER_APP = 100; /** The number of the most recently completed jobs to keep track of for debugging purposes. */ - private static final int NUM_COMPLETED_JOB_HISTORY = - Build.IS_USERDEBUG || Build.IS_ENG ? 25 : 0; + private static final int NUM_COMPLETED_JOB_HISTORY = 20; @VisibleForTesting public static Clock sSystemClock = Clock.systemUTC(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java index be91947b0445..2a23d60d8af6 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -271,7 +271,9 @@ public final class JobServiceContext implements ServiceConnection { if (job.shouldTreatAsExpeditedJob()) { // TODO(171305774): The job should run on the little cores. We'll probably need // another binding flag for that. - bindFlags = Context.BIND_AUTO_CREATE; + bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND + | Context.BIND_ALMOST_PERCEPTIBLE + | Context.BIND_ALLOW_NETWORK_ACCESS; } else { bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_PERCEPTIBLE; diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java index 824fa7fc1659..2faa8360bf44 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java @@ -828,8 +828,8 @@ public final class QuotaController extends StateController { } @NonNull - private ShrinkableDebits getEJQuotaLocked(final int userId, - @NonNull final String packageName) { + @VisibleForTesting + ShrinkableDebits getEJDebitsLocked(final int userId, @NonNull final String packageName) { ShrinkableDebits debits = mEJStats.get(userId, packageName); if (debits == null) { debits = new ShrinkableDebits( @@ -931,7 +931,7 @@ public final class QuotaController extends StateController { @VisibleForTesting long getRemainingEJExecutionTimeLocked(final int userId, @NonNull final String packageName) { - ShrinkableDebits quota = getEJQuotaLocked(userId, packageName); + ShrinkableDebits quota = getEJDebitsLocked(userId, packageName); if (quota.getStandbyBucketLocked() == NEVER_INDEX) { return 0; } @@ -948,7 +948,7 @@ public final class QuotaController extends StateController { if (ts.endTimeElapsed < windowStartTimeElapsed) { final long duration = ts.endTimeElapsed - ts.startTimeElapsed; remainingMs += duration; - quota.transactOnDebitsLocked(-duration); + quota.transactLocked(-duration); timingSessions.remove(0); } else if (ts.startTimeElapsed < windowStartTimeElapsed) { remainingMs += windowStartTimeElapsed - ts.startTimeElapsed; @@ -960,15 +960,16 @@ public final class QuotaController extends StateController { } } + TopAppTimer topAppTimer = mTopAppTrackers.get(userId, packageName); + if (topAppTimer != null && topAppTimer.isActive()) { + remainingMs += topAppTimer.getPendingReward(nowElapsed); + } + Timer timer = mEJPkgTimers.get(userId, packageName); if (timer == null) { return remainingMs; } - // There's a case where the debits tally is 0 but a currently running HPJ still counts - // towards quota. If the app gets a reward in this case, the reward is lost and the HPJ - // run is still fully counted. - // TODO(171305774)/STOPSHIP: make sure getting rewards while HPJ currently executing isn't - // treated negatively + return remainingMs - timer.getCurrentDuration(sElapsedRealtimeClock.millis()); } @@ -1081,7 +1082,7 @@ public final class QuotaController extends StateController { } final long nowElapsed = sElapsedRealtimeClock.millis(); - ShrinkableDebits quota = getEJQuotaLocked(userId, packageName); + ShrinkableDebits quota = getEJDebitsLocked(userId, packageName); final long limitMs = getEJLimitMsLocked(packageName, quota.getStandbyBucketLocked()); final long startWindowElapsed = Math.max(0, nowElapsed - mEJLimitWindowSizeMs); long remainingDeadSpaceMs = remainingExecutionTimeMs; @@ -1372,6 +1373,11 @@ public final class QuotaController extends StateController { @VisibleForTesting void saveTimingSession(final int userId, @NonNull final String packageName, @NonNull final TimingSession session, boolean isExpedited) { + saveTimingSession(userId, packageName, session, isExpedited, 0); + } + + private void saveTimingSession(final int userId, @NonNull final String packageName, + @NonNull final TimingSession session, boolean isExpedited, long debitAdjustment) { synchronized (mLock) { final SparseArrayMap<String, List<TimingSession>> sessionMap = isExpedited ? mEJTimingSessions : mTimingSessions; @@ -1382,8 +1388,9 @@ public final class QuotaController extends StateController { } sessions.add(session); if (isExpedited) { - final ShrinkableDebits quota = getEJQuotaLocked(userId, packageName); - quota.transactOnDebitsLocked(session.endTimeElapsed - session.startTimeElapsed); + final ShrinkableDebits quota = getEJDebitsLocked(userId, packageName); + quota.transactLocked(session.endTimeElapsed - session.startTimeElapsed + + debitAdjustment); } else { // Adding a new session means that the current stats are now incorrect. invalidateAllExecutionStatsLocked(userId, packageName); @@ -1396,15 +1403,34 @@ public final class QuotaController extends StateController { private void grantRewardForInstantEvent( final int userId, @NonNull final String packageName, final long credit) { synchronized (mLock) { - final ShrinkableDebits quota = getEJQuotaLocked(userId, packageName); - quota.transactOnDebitsLocked(-credit); - if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(), - userId, packageName)) { + final long nowElapsed = sElapsedRealtimeClock.millis(); + final ShrinkableDebits quota = getEJDebitsLocked(userId, packageName); + if (transactQuotaLocked(userId, packageName, nowElapsed, quota, credit) + && maybeUpdateConstraintForPkgLocked(nowElapsed, userId, packageName)) { mStateChangedListener.onControllerStateChanged(); } } } + private boolean transactQuotaLocked(final int userId, @NonNull final String packageName, + final long nowElapsed, @NonNull ShrinkableDebits debits, final long credit) { + final long oldTally = debits.getTallyLocked(); + final long leftover = debits.transactLocked(-credit); + if (DEBUG) { + Slog.d(TAG, "debits overflowed by " + leftover); + } + boolean changed = oldTally != debits.getTallyLocked(); + if (leftover != 0) { + // Only adjust timer if its active. + final Timer ejTimer = mEJPkgTimers.get(userId, packageName); + if (ejTimer != null && ejTimer.isActive()) { + ejTimer.updateDebitAdjustment(nowElapsed, leftover); + changed = true; + } + } + return changed; + } + private final class EarliestEndTimeFunctor implements Consumer<List<TimingSession>> { public long earliestEndElapsed = Long.MAX_VALUE; @@ -1875,7 +1901,8 @@ public final class QuotaController extends StateController { } } - private static final class ShrinkableDebits { + @VisibleForTesting + static final class ShrinkableDebits { /** The amount of quota remaining. Can be negative if limit changes. */ private long mDebitTally; private int mStandbyBucket; @@ -1893,8 +1920,11 @@ public final class QuotaController extends StateController { * Negative if the tally should decrease (therefore increasing available quota); * or positive if the tally should increase (therefore decreasing available quota). */ - void transactOnDebitsLocked(final long amount) { + long transactLocked(final long amount) { + final long leftover = amount < 0 && Math.abs(amount) > mDebitTally + ? mDebitTally + amount : 0; mDebitTally = Math.max(0, mDebitTally + amount); + return leftover; } void setStandbyBucketLocked(int standbyBucket) { @@ -1927,6 +1957,7 @@ public final class QuotaController extends StateController { private final ArraySet<JobStatus> mRunningBgJobs = new ArraySet<>(); private long mStartTimeElapsed; private int mBgJobCount; + private long mDebitAdjustment; Timer(int uid, int userId, String packageName, boolean regularJobTimer) { mPkg = new Package(userId, packageName); @@ -1957,6 +1988,7 @@ public final class QuotaController extends StateController { if (mRunningBgJobs.size() == 1) { // Started tracking the first job. mStartTimeElapsed = sElapsedRealtimeClock.millis(); + mDebitAdjustment = 0; if (mRegularJobTimer) { // Starting the timer means that all cached execution stats are now // incorrect. @@ -1988,6 +2020,11 @@ public final class QuotaController extends StateController { } } + void updateDebitAdjustment(long nowElapsed, long debit) { + // Make sure we don't have a credit larger than the expected session. + mDebitAdjustment = Math.max(mDebitAdjustment + debit, mStartTimeElapsed - nowElapsed); + } + /** * Stops tracking all jobs and cancels any pending alarms. This should only be called if * the Timer is not going to be used anymore. @@ -2003,7 +2040,8 @@ public final class QuotaController extends StateController { return; } TimingSession ts = new TimingSession(mStartTimeElapsed, nowElapsed, mBgJobCount); - saveTimingSession(mPkg.userId, mPkg.packageName, ts, !mRegularJobTimer); + saveTimingSession(mPkg.userId, mPkg.packageName, ts, !mRegularJobTimer, + mDebitAdjustment); mBgJobCount = 0; // Don't reset the tracked jobs list as we need to keep tracking the current number // of jobs. @@ -2030,7 +2068,7 @@ public final class QuotaController extends StateController { long getCurrentDuration(long nowElapsed) { synchronized (mLock) { - return !isActive() ? 0 : nowElapsed - mStartTimeElapsed; + return !isActive() ? 0 : nowElapsed - mStartTimeElapsed + mDebitAdjustment; } } @@ -2059,6 +2097,7 @@ public final class QuotaController extends StateController { // Start timing from unplug. if (mRunningBgJobs.size() > 0) { mStartTimeElapsed = nowElapsed; + mDebitAdjustment = 0; // NOTE: this does have the unfortunate consequence that if the device is // repeatedly plugged in and unplugged, or an app changes foreground state // very frequently, the job count for a package may be artificially high. @@ -2128,6 +2167,11 @@ public final class QuotaController extends StateController { pw.print(", "); pw.print(mBgJobCount); pw.print(" running bg jobs"); + if (!mRegularJobTimer) { + pw.print(" (debit adj="); + pw.print(mDebitAdjustment); + pw.print(")"); + } pw.println(); pw.increaseIndent(); for (int i = 0; i < mRunningBgJobs.size(); i++) { @@ -2171,6 +2215,21 @@ public final class QuotaController extends StateController { mPkg = new Package(userId, packageName); } + private int calculateTimeChunks(final long nowElapsed) { + final long totalTopTimeMs = nowElapsed - mStartTimeElapsed; + int numTimeChunks = (int) (totalTopTimeMs / mEJTopAppTimeChunkSizeMs); + final long remainderMs = totalTopTimeMs % mEJTopAppTimeChunkSizeMs; + if (remainderMs >= SECOND_IN_MILLIS) { + // "Round up" + numTimeChunks++; + } + return numTimeChunks; + } + + long getPendingReward(final long nowElapsed) { + return mEJRewardTopAppMs * calculateTimeChunks(nowElapsed); + } + void processEventLocked(@NonNull UsageEvents.Event event) { final long nowElapsed = sElapsedRealtimeClock.millis(); switch (event.getEventType()) { @@ -2186,21 +2245,16 @@ public final class QuotaController extends StateController { final UsageEvents.Event existingEvent = mActivities.removeReturnOld(event.mInstanceId); if (existingEvent != null && mActivities.size() == 0) { - final long totalTopTimeMs = nowElapsed - mStartTimeElapsed; - int numTimeChunks = (int) (totalTopTimeMs / mEJTopAppTimeChunkSizeMs); - final long remainderMs = totalTopTimeMs % mEJTopAppTimeChunkSizeMs; - if (remainderMs >= SECOND_IN_MILLIS) { - // "Round up" - numTimeChunks++; - } + final long pendingReward = getPendingReward(nowElapsed); if (DEBUG) { - Slog.d(TAG, - "Crediting " + mPkg + " for " + numTimeChunks + " time chunks"); + Slog.d(TAG, "Crediting " + mPkg + " " + pendingReward + "ms" + + " for " + calculateTimeChunks(nowElapsed) + " time chunks"); } - final ShrinkableDebits quota = - getEJQuotaLocked(mPkg.userId, mPkg.packageName); - quota.transactOnDebitsLocked(-mEJRewardTopAppMs * numTimeChunks); - if (maybeUpdateConstraintForPkgLocked(nowElapsed, + final ShrinkableDebits debits = + getEJDebitsLocked(mPkg.userId, mPkg.packageName); + if (transactQuotaLocked(mPkg.userId, mPkg.packageName, + nowElapsed, debits, pendingReward) + && maybeUpdateConstraintForPkgLocked(nowElapsed, mPkg.userId, mPkg.packageName)) { mStateChangedListener.onControllerStateChanged(); } @@ -2321,7 +2375,7 @@ public final class QuotaController extends StateController { */ @Override public void onUsageEvent(int userId, @NonNull UsageEvents.Event event) { - mHandler.obtainMessage(MSG_PROCESS_USAGE_EVENT, userId, 0, event); + mHandler.obtainMessage(MSG_PROCESS_USAGE_EVENT, userId, 0, event).sendToTarget(); } } @@ -2408,7 +2462,6 @@ public final class QuotaController extends StateController { } private class QcHandler extends Handler { - private boolean mIsProcessing; QcHandler(Looper looper) { super(looper); @@ -2417,8 +2470,6 @@ public final class QuotaController extends StateController { @Override public void handleMessage(Message msg) { synchronized (mLock) { - mIsProcessing = true; - switch (msg.what) { case MSG_REACHED_QUOTA: { Package pkg = (Package) msg.obj; @@ -2539,6 +2590,10 @@ public final class QuotaController extends StateController { final int userId = msg.arg1; final UsageEvents.Event event = (UsageEvents.Event) msg.obj; final String pkgName = event.getPackageName(); + if (DEBUG) { + Slog.d(TAG, "Processing event " + event.getEventType() + + " for " + string(userId, pkgName)); + } switch (event.getEventType()) { case UsageEvents.Event.ACTIVITY_RESUMED: case UsageEvents.Event.ACTIVITY_PAUSED: @@ -2604,8 +2659,6 @@ public final class QuotaController extends StateController { } } } - - mIsProcessing = false; } } @@ -3883,11 +3936,6 @@ public final class QuotaController extends StateController { return mQcConstants; } - @VisibleForTesting - boolean isActiveBackgroundProcessing() { - return mHandler.mIsProcessing; - } - //////////////////////////// DATA DUMP ////////////////////////////// @Override diff --git a/apex/jobscheduler/service/jni/Android.bp b/apex/jobscheduler/service/jni/Android.bp index 4bcc165e9eea..c630217387ce 100644 --- a/apex/jobscheduler/service/jni/Android.bp +++ b/apex/jobscheduler/service/jni/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + cc_library_shared { name: "libalarm_jni", diff --git a/apex/media/service/Android.bp b/apex/media/service/Android.bp index 5b24cfa4219b..9b3399e8b0e1 100644 --- a/apex/media/service/Android.bp +++ b/apex/media/service/Android.bp @@ -11,6 +11,15 @@ // 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + filegroup { name: "service-media-s-sources", srcs: [ @@ -38,4 +47,3 @@ java_sdk_library { "com.android.media", ], } - diff --git a/cmds/abx/Android.bp b/cmds/abx/Android.bp index 333acedfadad..50a0b75b3276 100644 --- a/cmds/abx/Android.bp +++ b/cmds/abx/Android.bp @@ -1,4 +1,21 @@ +package { + default_applicable_licenses: ["frameworks_base_cmds_abx_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_abx_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + java_binary { name: "abx", wrapper: "abx", diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index 4b7eda096e54..ed717c491467 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -19,7 +19,6 @@ package com.android.commands.bmgr; import android.annotation.IntDef; import android.annotation.UserIdInt; import android.app.backup.BackupManager; -import android.app.backup.BackupManager.OperationType; import android.app.backup.BackupManagerMonitor; import android.app.backup.BackupProgress; import android.app.backup.BackupTransport; @@ -667,7 +666,7 @@ public class Bmgr { // The rest of the 'list' options work with a restore session on the current transport try { - mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null, OperationType.BACKUP); + mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null); if (mRestore == null) { System.err.println(BMGR_ERR_NO_RESTORESESSION_FOR_USER + userId); return; @@ -822,7 +821,7 @@ public class Bmgr { try { boolean didRestore = false; - mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null, OperationType.BACKUP); + mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null); if (mRestore == null) { System.err.println(BMGR_ERR_NO_RESTORESESSION_FOR_USER + userId); return; diff --git a/cmds/hid/OWNERS b/cmds/hid/OWNERS new file mode 100644 index 000000000000..d701f23cb9b8 --- /dev/null +++ b/cmds/hid/OWNERS @@ -0,0 +1 @@ +include /core/java/android/hardware/input/OWNERS diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp index 437a87e8df54..2cda57dd67e9 100644 --- a/cmds/hid/jni/com_android_commands_hid_Device.cpp +++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp @@ -48,6 +48,7 @@ static const char* UHID_PATH = "/dev/uhid"; static struct { jmethodID onDeviceOpen; jmethodID onDeviceGetReport; + jmethodID onDeviceSetReport; jmethodID onDeviceOutput; jmethodID onDeviceError; } gDeviceCallbackClassInfo; @@ -113,7 +114,16 @@ void DeviceCallback::onDeviceGetReport(uint32_t requestId, uint8_t reportId) { checkAndClearException(env, "onDeviceGetReport"); } -void DeviceCallback::onDeviceOutput(uint8_t rType, const std::vector<uint8_t>& data) { +void DeviceCallback::onDeviceSetReport(uint8_t rType, + const std::vector<uint8_t>& data) { + JNIEnv* env = getJNIEnv(); + env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceSetReport, rType, + toJbyteArray(env, data).get()); + checkAndClearException(env, "onDeviceSetReport"); +} + +void DeviceCallback::onDeviceOutput(uint8_t rType, + const std::vector<uint8_t>& data) { JNIEnv* env = getJNIEnv(); env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOutput, rType, toJbyteArray(env, data).get()); @@ -261,6 +271,7 @@ int Device::handleEvents(int events) { ALOGD("Received SET_REPORT: id=%" PRIu32 " rnum=%" PRIu8 " data=%s", set_report.id, set_report.rnum, toString(data).c_str()); } + mDeviceCallback->onDeviceSetReport(set_report.rtype, data); break; } case UHID_OUTPUT: { @@ -364,6 +375,8 @@ int register_com_android_commands_hid_Device(JNIEnv* env) { env->GetMethodID(clazz, "onDeviceOpen", "()V"); uhid::gDeviceCallbackClassInfo.onDeviceGetReport = env->GetMethodID(clazz, "onDeviceGetReport", "(II)V"); + uhid::gDeviceCallbackClassInfo.onDeviceSetReport = + env->GetMethodID(clazz, "onDeviceSetReport", "(B[B)V"); uhid::gDeviceCallbackClassInfo.onDeviceOutput = env->GetMethodID(clazz, "onDeviceOutput", "(B[B)V"); uhid::gDeviceCallbackClassInfo.onDeviceError = diff --git a/cmds/hid/jni/com_android_commands_hid_Device.h b/cmds/hid/jni/com_android_commands_hid_Device.h index 5483b40831a0..d10a9aa3680c 100644 --- a/cmds/hid/jni/com_android_commands_hid_Device.h +++ b/cmds/hid/jni/com_android_commands_hid_Device.h @@ -31,6 +31,7 @@ public: void onDeviceOpen(); void onDeviceGetReport(uint32_t requestId, uint8_t reportId); + void onDeviceSetReport(uint8_t rType, const std::vector<uint8_t>& data); void onDeviceOutput(uint8_t rType, const std::vector<uint8_t>& data); void onDeviceError(); diff --git a/cmds/hid/src/com/android/commands/hid/Device.java b/cmds/hid/src/com/android/commands/hid/Device.java index 20b4bd86baec..95b1e9a7b57f 100644 --- a/cmds/hid/src/com/android/commands/hid/Device.java +++ b/cmds/hid/src/com/android/commands/hid/Device.java @@ -46,7 +46,8 @@ public class Device { // Sync with linux uhid_event_type::UHID_OUTPUT private static final byte UHID_EVENT_TYPE_UHID_OUTPUT = 6; - + // Sync with linux uhid_event_type::UHID_SET_REPORT + private static final byte UHID_EVENT_TYPE_SET_REPORT = 13; private final int mId; private final HandlerThread mThread; private final DeviceHandler mHandler; @@ -198,11 +199,11 @@ public class Device { mHandler.sendMessageAtTime(msg, mTimeToSend); } - // native callback - public void onDeviceOutput(byte rtype, byte[] data) { + // Send out the report to HID command output + private void sendReportOutput(byte eventId, byte rtype, byte[] data) { JSONObject json = new JSONObject(); try { - json.put("eventId", UHID_EVENT_TYPE_UHID_OUTPUT); + json.put("eventId", eventId); json.put("deviceId", mId); json.put("reportType", rtype); JSONArray dataArray = new JSONArray(); @@ -220,6 +221,18 @@ public class Device { throw new RuntimeException(e); } + } + + // native callback + public void onDeviceSetReport(byte rtype, byte[] data) { + // We don't need to reply for the SET_REPORT but just send it to HID output for test + // verification. + sendReportOutput(UHID_EVENT_TYPE_SET_REPORT, rtype, data); + } + + // native callback + public void onDeviceOutput(byte rtype, byte[] data) { + sendReportOutput(UHID_EVENT_TYPE_UHID_OUTPUT, rtype, data); if (mOutputs == null) { Log.e(TAG, "Received OUTPUT request, but 'outputs' section is not found"); return; diff --git a/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java b/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java index a0361d0a39d3..ca9df39f83bf 100644 --- a/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java +++ b/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java @@ -182,6 +182,8 @@ public class RequestSync { mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true); } else if (opt.equals("--rc") || opt.equals("--require-charging")) { mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_REQUIRE_CHARGING, true); + } else if (opt.equals("--ej") || opt.equals("--schedule-as-ej")) { + mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, true); } else if (opt.equals("-e") || opt.equals("--es") || opt.equals("--extra-string")) { final String key = nextArgRequired(); final String value = nextArgRequired(); diff --git a/cmds/uinput/Android.bp b/cmds/uinput/Android.bp index 0d7fed2a15c7..260cfc781ebc 100644 --- a/cmds/uinput/Android.bp +++ b/cmds/uinput/Android.bp @@ -1,6 +1,23 @@ // Copyright 2020 The Android Open Source Project // +package { + default_applicable_licenses: ["frameworks_base_cmds_uinput_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_uinput_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + java_binary { name: "uinput", wrapper: "uinput", @@ -15,4 +32,4 @@ filegroup { srcs: [ "src/com/android/commands/uinput/InputAbsInfo.aidl", ], -}
\ No newline at end of file +} diff --git a/cmds/uinput/jni/Android.bp b/cmds/uinput/jni/Android.bp index 199bbbd35274..c56adc35b580 100644 --- a/cmds/uinput/jni/Android.bp +++ b/cmds/uinput/jni/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_cmds_uinput_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_cmds_uinput_license"], +} + cc_library_shared { name: "libuinputcommand_jni", diff --git a/core/api/current.txt b/core/api/current.txt index b0375b69553d..08c7a43294c3 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -507,6 +507,7 @@ package android { field public static final int dashGap = 16843175; // 0x10101a7 field public static final int dashWidth = 16843174; // 0x10101a6 field public static final int data = 16842798; // 0x101002e + field public static final int dataExtractionRules = 16844350; // 0x101063e field public static final int datePickerDialogTheme = 16843948; // 0x10104ac field public static final int datePickerMode = 16843955; // 0x10104b3 field public static final int datePickerStyle = 16843612; // 0x101035c @@ -528,6 +529,8 @@ package android { field public static final int detailSocialSummary = 16843428; // 0x10102a4 field public static final int detailsElementBackground = 16843598; // 0x101034e field public static final int dial = 16843010; // 0x1010102 + field public static final int dialTint = 16844342; // 0x1010636 + field public static final int dialTintMode = 16844343; // 0x1010637 field public static final int dialogCornerRadius = 16844145; // 0x1010571 field public static final int dialogIcon = 16843252; // 0x10101f4 field public static final int dialogLayout = 16843255; // 0x10101f7 @@ -725,8 +728,14 @@ package android { field public static final int groupIndicator = 16843019; // 0x101010b field public static final int gwpAsanMode = 16844310; // 0x1010616 field public static final int hand_hour = 16843011; // 0x1010103 + field public static final int hand_hourTint = 16844344; // 0x1010638 + field public static final int hand_hourTintMode = 16844345; // 0x1010639 field public static final int hand_minute = 16843012; // 0x1010104 + field public static final int hand_minuteTint = 16844346; // 0x101063a + field public static final int hand_minuteTintMode = 16844347; // 0x101063b field public static final int hand_second = 16844323; // 0x1010623 + field public static final int hand_secondTint = 16844348; // 0x101063c + field public static final int hand_secondTintMode = 16844349; // 0x101063d field public static final int handle = 16843354; // 0x101025a field public static final int handleProfiling = 16842786; // 0x1010022 field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e @@ -4735,6 +4744,13 @@ package android.app { field @NonNull public static final android.os.Parcelable.Creator<android.app.AutomaticZenRule> CREATOR; } + public final class BackgroundServiceStartNotAllowedException extends android.app.ServiceStartNotAllowedException implements android.os.Parcelable { + ctor public BackgroundServiceStartNotAllowedException(@NonNull String); + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.BackgroundServiceStartNotAllowedException> CREATOR; + } + public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener { ctor public DatePickerDialog(@NonNull android.content.Context); ctor public DatePickerDialog(@NonNull android.content.Context, @StyleRes int); @@ -4980,6 +4996,13 @@ package android.app { method @Deprecated public void setSelectedGroup(int); } + public final class ForegroundServiceStartNotAllowedException extends android.app.ServiceStartNotAllowedException implements android.os.Parcelable { + ctor public ForegroundServiceStartNotAllowedException(@NonNull String); + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.ForegroundServiceStartNotAllowedException> CREATOR; + } + @Deprecated public class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener { ctor @Deprecated public Fragment(); method @Deprecated public void dump(String, java.io.FileDescriptor, java.io.PrintWriter, String[]); @@ -5588,6 +5611,7 @@ package android.app { field public static final int DEFAULT_LIGHTS = 4; // 0x4 field public static final int DEFAULT_SOUND = 1; // 0x1 field public static final int DEFAULT_VIBRATE = 2; // 0x2 + field public static final String EXTRA_ANSWER_COLOR = "android.answerColor"; field public static final String EXTRA_ANSWER_INTENT = "android.answerIntent"; field public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents"; field public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri"; @@ -5599,6 +5623,7 @@ package android.app { field public static final String EXTRA_COLORIZED = "android.colorized"; field public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions"; field public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle"; + field public static final String EXTRA_DECLINE_COLOR = "android.declineColor"; field public static final String EXTRA_DECLINE_INTENT = "android.declineIntent"; field public static final String EXTRA_HANG_UP_INTENT = "android.hangUpIntent"; field public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic"; @@ -5886,6 +5911,8 @@ package android.app { method @NonNull public static android.app.Notification.CallStyle forIncomingCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent, @NonNull android.app.PendingIntent); method @NonNull public static android.app.Notification.CallStyle forOngoingCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent); method @NonNull public static android.app.Notification.CallStyle forScreeningCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent, @NonNull android.app.PendingIntent); + method @NonNull public android.app.Notification.CallStyle setAnswerButtonColorHint(@ColorInt int); + method @NonNull public android.app.Notification.CallStyle setDeclineButtonColorHint(@ColorInt int); method @NonNull public android.app.Notification.CallStyle setVerificationIcon(@Nullable android.graphics.drawable.Icon); method @NonNull public android.app.Notification.CallStyle setVerificationText(@Nullable CharSequence); } @@ -6563,6 +6590,9 @@ package android.app { field public static final int STOP_FOREGROUND_REMOVE = 1; // 0x1 } + public abstract class ServiceStartNotAllowedException extends java.lang.IllegalStateException { + } + public abstract class SharedElementCallback { ctor public SharedElementCallback(); method public android.os.Parcelable onCaptureSharedElementSnapshot(android.view.View, android.graphics.Matrix, android.graphics.RectF); @@ -8346,7 +8376,9 @@ package android.appwidget { method protected android.view.View getDefaultView(); method protected android.view.View getErrorView(); method protected void prepareView(android.view.View); + method public void resetColorResources(); method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo); + method public void setColorResources(@NonNull android.util.SparseIntArray); method public void setCurrentSize(@NonNull android.graphics.PointF); method public void setExecutor(java.util.concurrent.Executor); method public void setOnLightBackground(boolean); @@ -8427,7 +8459,7 @@ package android.appwidget { method public int describeContents(); method public final android.os.UserHandle getProfile(); method @NonNull public android.content.pm.ActivityInfo getProviderInfo(); - method @Nullable public final String loadDescription(@NonNull android.content.Context); + method @Nullable public final CharSequence loadDescription(@NonNull android.content.Context); method public final android.graphics.drawable.Drawable loadIcon(@NonNull android.content.Context, int); method public final String loadLabel(android.content.pm.PackageManager); method public final android.graphics.drawable.Drawable loadPreviewImage(@NonNull android.content.Context, int); @@ -8445,7 +8477,7 @@ package android.appwidget { field public static final int WIDGET_FEATURE_RECONFIGURABLE = 1; // 0x1 field public int autoAdvanceViewId; field public android.content.ComponentName configure; - field @IdRes public int descriptionResource; + field @IdRes public int descriptionRes; field public int icon; field public int initialKeyguardLayout; field public int initialLayout; @@ -10213,6 +10245,7 @@ package android.content { field public static final String SYNC_EXTRAS_MANUAL = "force"; field public static final String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override"; field public static final String SYNC_EXTRAS_REQUIRE_CHARGING = "require_charging"; + field public static final String SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB = "schedule_as_expedited_job"; field public static final String SYNC_EXTRAS_UPLOAD = "upload"; field public static final int SYNC_OBSERVER_TYPE_ACTIVE = 4; // 0x4 field public static final int SYNC_OBSERVER_TYPE_PENDING = 2; // 0x2 @@ -10356,7 +10389,7 @@ package android.content { method public abstract void grantUriPermission(String, android.net.Uri, int); method public abstract boolean isDeviceProtectedStorage(); method public boolean isRestricted(); - method public static boolean isUiContext(@NonNull android.content.Context); + method public boolean isUiContext(); method public abstract boolean moveDatabaseFrom(android.content.Context, String); method public abstract boolean moveSharedPreferencesFrom(android.content.Context, String); method @NonNull public final android.content.res.TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[]); @@ -11535,6 +11568,7 @@ package android.content { method public android.content.SyncRequest.Builder setManual(boolean); method public android.content.SyncRequest.Builder setNoRetry(boolean); method public android.content.SyncRequest.Builder setRequiresCharging(boolean); + method @NonNull public android.content.SyncRequest.Builder setScheduleAsExpeditedJob(boolean); method public android.content.SyncRequest.Builder setSyncAdapter(android.accounts.Account, String); method public android.content.SyncRequest.Builder syncOnce(); method public android.content.SyncRequest.Builder syncPeriodic(long, long); @@ -18534,23 +18568,6 @@ package android.hardware.camera2.params { package android.hardware.display { - public final class DeviceProductInfo implements android.os.Parcelable { - method public int describeContents(); - method public int getConnectionToSinkType(); - method public int getManufactureWeek(); - method public int getManufactureYear(); - method @NonNull public String getManufacturerPnpId(); - method public int getModelYear(); - method @Nullable public String getName(); - method @NonNull public String getProductId(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field public static final int CONNECTION_TO_SINK_BUILT_IN = 1; // 0x1 - field public static final int CONNECTION_TO_SINK_DIRECT = 2; // 0x2 - field public static final int CONNECTION_TO_SINK_TRANSITIVE = 3; // 0x3 - field public static final int CONNECTION_TO_SINK_UNKNOWN = 0; // 0x0 - field @NonNull public static final android.os.Parcelable.Creator<android.hardware.display.DeviceProductInfo> CREATOR; - } - public final class DisplayManager { method public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, int, int, int, @Nullable android.view.Surface, int); method public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, int, int, int, @Nullable android.view.Surface, int, @Nullable android.hardware.display.VirtualDisplay.Callback, @Nullable android.os.Handler); @@ -18659,6 +18676,58 @@ package android.hardware.input { } +package android.hardware.lights { + + public final class Light implements android.os.Parcelable { + method public int describeContents(); + method public int getId(); + method @NonNull public String getName(); + method public int getOrdinal(); + method public int getType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.Light> CREATOR; + field public static final int LIGHT_TYPE_INPUT_PLAYER_ID = 10; // 0xa + field public static final int LIGHT_TYPE_INPUT_RGB = 11; // 0xb + field public static final int LIGHT_TYPE_INPUT_SINGLE = 9; // 0x9 + field public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8 + } + + public final class LightState implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public static android.hardware.lights.LightState forColor(@ColorInt int); + method @NonNull public static android.hardware.lights.LightState forPlayerId(int); + method @ColorInt public int getColor(); + method public int getPlayerId(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.LightState> CREATOR; + } + + public abstract class LightsManager { + method @NonNull public abstract android.hardware.lights.LightState getLightState(@NonNull android.hardware.lights.Light); + method @NonNull public abstract java.util.List<android.hardware.lights.Light> getLights(); + method @NonNull public abstract android.hardware.lights.LightsManager.LightsSession openSession(); + } + + public abstract static class LightsManager.LightsSession implements java.lang.AutoCloseable { + ctor public LightsManager.LightsSession(); + method public abstract void close(); + method public abstract void requestLights(@NonNull android.hardware.lights.LightsRequest); + } + + public final class LightsRequest { + method @NonNull public java.util.List<android.hardware.lights.LightState> getLightStates(); + method @NonNull public java.util.List<java.lang.Integer> getLights(); + } + + public static final class LightsRequest.Builder { + ctor public LightsRequest.Builder(); + method @NonNull public android.hardware.lights.LightsRequest.Builder addLight(@NonNull android.hardware.lights.Light, @NonNull android.hardware.lights.LightState); + method @NonNull public android.hardware.lights.LightsRequest build(); + method @NonNull public android.hardware.lights.LightsRequest.Builder clearLight(@NonNull android.hardware.lights.Light); + } + +} + package android.hardware.usb { public class UsbAccessory implements android.os.Parcelable { @@ -18827,6 +18896,7 @@ package android.inputmethodservice { public abstract class AbstractInputMethodService extends android.app.Service implements android.view.KeyEvent.Callback { ctor public AbstractInputMethodService(); method public android.view.KeyEvent.DispatcherState getKeyDispatcherState(); + method public final boolean isUiContext(); method public final android.os.IBinder onBind(android.content.Intent); method public abstract android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodImpl onCreateInputMethodInterface(); method public abstract android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface(); @@ -26219,6 +26289,7 @@ package android.net { field public static final int NET_CAPABILITY_CBS = 5; // 0x5 field public static final int NET_CAPABILITY_DUN = 2; // 0x2 field public static final int NET_CAPABILITY_EIMS = 10; // 0xa + field public static final int NET_CAPABILITY_ENTERPRISE = 29; // 0x1d field public static final int NET_CAPABILITY_FOREGROUND = 19; // 0x13 field public static final int NET_CAPABILITY_FOTA = 3; // 0x3 field public static final int NET_CAPABILITY_IA = 7; // 0x7 @@ -30586,8 +30657,8 @@ package android.os { } public final class BugreportManager { - method public void cancelBugreport(); - method public void startConnectivityBugreport(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback); + method @WorkerThread public void cancelBugreport(); + method @WorkerThread public void startConnectivityBugreport(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback); } public abstract static class BugreportManager.BugreportCallback { @@ -35049,6 +35120,7 @@ package android.provider { field public static final String ACTION_QUICK_ACCESS_WALLET_SETTINGS = "android.settings.QUICK_ACCESS_WALLET_SETTINGS"; field public static final String ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS"; field public static final String ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"; + field public static final String ACTION_REQUEST_SCHEDULE_EXACT_ALARM = "android.settings.REQUEST_SCHEDULE_EXACT_ALARM"; field public static final String ACTION_REQUEST_SET_AUTOFILL_SERVICE = "android.settings.REQUEST_SET_AUTOFILL_SERVICE"; field public static final String ACTION_SEARCH_SETTINGS = "android.search.action.SEARCH_SETTINGS"; field public static final String ACTION_SECURITY_SETTINGS = "android.settings.SECURITY_SETTINGS"; @@ -37093,8 +37165,10 @@ package android.security { method @NonNull public static android.content.Intent createInstallIntent(); method @NonNull public static android.content.Intent createManageCredentialsIntent(@NonNull android.security.AppUriAuthenticationPolicy); method @Nullable @WorkerThread public static java.security.cert.X509Certificate[] getCertificateChain(@NonNull android.content.Context, @NonNull String) throws java.lang.InterruptedException, android.security.KeyChainException; + method @NonNull public static android.security.AppUriAuthenticationPolicy getCredentialManagementAppPolicy(@NonNull android.content.Context) throws java.lang.SecurityException; method @Nullable @WorkerThread public static java.security.PrivateKey getPrivateKey(@NonNull android.content.Context, @NonNull String) throws java.lang.InterruptedException, android.security.KeyChainException; method @Deprecated public static boolean isBoundKeyAlgorithm(@NonNull String); + method public static boolean isCredentialManagementApp(@NonNull android.content.Context); method public static boolean isKeyAlgorithmSupported(@NonNull String); field public static final String ACTION_KEYCHAIN_CHANGED = "android.security.action.KEYCHAIN_CHANGED"; field public static final String ACTION_KEY_ACCESS_CHANGED = "android.security.action.KEY_ACCESS_CHANGED"; @@ -41165,6 +41239,7 @@ package android.telephony { field public static final int ACTIVATION_REJECT_GGSN = 30; // 0x1e field public static final int ACTIVATION_REJECT_UNSPECIFIED = 31; // 0x1f field public static final int ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED = 65; // 0x41 + field public static final int ALL_MATCHING_RULES_FAILED = 2254; // 0x8ce field public static final int APN_DISABLED = 2045; // 0x7fd field public static final int APN_DISALLOWED_ON_ROAMING = 2059; // 0x80b field public static final int APN_MISMATCH = 2054; // 0x806 @@ -41314,6 +41389,7 @@ package android.telephony { field public static final int LTE_NAS_SERVICE_REQUEST_FAILED = 2117; // 0x845 field public static final int LTE_THROTTLING_NOT_REQUIRED = 2127; // 0x84f field public static final int MAC_FAILURE = 2183; // 0x887 + field public static final int MATCH_ALL_RULE_NOT_ALLOWED = 2253; // 0x8cd field public static final int MAXIMIUM_NSAPIS_EXCEEDED = 2157; // 0x86d field public static final int MAXINUM_SIZE_OF_L2_MESSAGE_EXCEEDED = 2166; // 0x876 field public static final int MAX_ACCESS_PROBE = 2079; // 0x81f @@ -42099,6 +42175,7 @@ package android.telephony { method @NonNull public android.telephony.SmsManager createForSubscriptionId(int); method public java.util.ArrayList<java.lang.String> divideMessage(String); method public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent); + method public void downloadMultimediaMessage(@NonNull android.content.Context, @NonNull String, @NonNull android.net.Uri, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent, long); method @NonNull public android.os.Bundle getCarrierConfigValues(); method @Deprecated public static android.telephony.SmsManager getDefault(); method public static int getDefaultSmsSubscriptionId(); @@ -42110,6 +42187,7 @@ package android.telephony { method public void injectSmsPdu(byte[], String, android.app.PendingIntent); method public void sendDataMessage(String, String, short, byte[], android.app.PendingIntent, android.app.PendingIntent); method public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent); + method public void sendMultimediaMessage(@NonNull android.content.Context, @NonNull android.net.Uri, @Nullable String, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent, long); method public void sendMultipartTextMessage(String, String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>); method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, long); method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String, @Nullable String); @@ -46627,7 +46705,6 @@ package android.view { method public long getAppVsyncOffsetNanos(); method public void getCurrentSizeRange(android.graphics.Point, android.graphics.Point); method @Nullable public android.view.DisplayCutout getCutout(); - method @Nullable public android.hardware.display.DeviceProductInfo getDeviceProductInfo(); method public int getDisplayId(); method public int getFlags(); method public android.view.Display.HdrCapabilities getHdrCapabilities(); @@ -46898,6 +46975,7 @@ package android.view { method public int getId(); method public android.view.KeyCharacterMap getKeyCharacterMap(); method public int getKeyboardType(); + method @NonNull public android.hardware.lights.LightsManager getLightsManager(); method public android.view.InputDevice.MotionRange getMotionRange(int); method public android.view.InputDevice.MotionRange getMotionRange(int, int); method public java.util.List<android.view.InputDevice.MotionRange> getMotionRanges(); @@ -51631,6 +51709,7 @@ package android.view.inputmethod { method public int describeContents(); method public void dump(android.util.Printer, String); method public android.content.ComponentName getComponent(); + method public int getConfigChanges(); method public String getId(); method public int getIsDefaultResourceId(); method public String getPackageName(); @@ -53312,6 +53391,7 @@ package android.widget { method public int getCheckedItemPosition(); method public android.util.SparseBooleanArray getCheckedItemPositions(); method public int getChoiceMode(); + method public int getEdgeEffectType(); method public int getListPaddingBottom(); method public int getListPaddingLeft(); method public int getListPaddingRight(); @@ -53353,6 +53433,7 @@ package android.widget { method public void setChoiceMode(int); method public void setDrawSelectorOnTop(boolean); method public void setEdgeEffectColor(@ColorInt int); + method public void setEdgeEffectType(int); method public void setFastScrollAlwaysVisible(boolean); method public void setFastScrollEnabled(boolean); method public void setFastScrollStyle(int); @@ -53643,11 +53724,27 @@ package android.widget { ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet); ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet, int); ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet, int, int); + method @Deprecated @Nullable public android.graphics.BlendMode getDialTintBlendMode(); + method @Deprecated @Nullable public android.content.res.ColorStateList getDialTintList(); + method @Deprecated @Nullable public android.graphics.BlendMode getHourHandTintBlendMode(); + method @Deprecated @Nullable public android.content.res.ColorStateList getHourHandTintList(); + method @Deprecated @Nullable public android.graphics.BlendMode getMinuteHandTintBlendMode(); + method @Deprecated @Nullable public android.content.res.ColorStateList getMinuteHandTintList(); + method @Deprecated @Nullable public android.graphics.BlendMode getSecondHandTintBlendMode(); + method @Deprecated @Nullable public android.content.res.ColorStateList getSecondHandTintList(); method @Deprecated @Nullable public String getTimeZone(); method @Deprecated public void setDial(@NonNull android.graphics.drawable.Icon); + method @Deprecated public void setDialTintBlendMode(@Nullable android.graphics.BlendMode); + method @Deprecated public void setDialTintList(@Nullable android.content.res.ColorStateList); method @Deprecated public void setHourHand(@NonNull android.graphics.drawable.Icon); + method @Deprecated public void setHourHandTintBlendMode(@Nullable android.graphics.BlendMode); + method @Deprecated public void setHourHandTintList(@Nullable android.content.res.ColorStateList); method @Deprecated public void setMinuteHand(@NonNull android.graphics.drawable.Icon); + method @Deprecated public void setMinuteHandTintBlendMode(@Nullable android.graphics.BlendMode); + method @Deprecated public void setMinuteHandTintList(@Nullable android.content.res.ColorStateList); method @Deprecated public void setSecondHand(@Nullable android.graphics.drawable.Icon); + method @Deprecated public void setSecondHandTintBlendMode(@Nullable android.graphics.BlendMode); + method @Deprecated public void setSecondHandTintList(@Nullable android.content.res.ColorStateList); method @Deprecated public void setTimeZone(@Nullable String); } @@ -54345,6 +54442,7 @@ package android.widget { method public boolean executeKeyEvent(android.view.KeyEvent); method public void fling(int); method public boolean fullScroll(int); + method public int getEdgeEffectType(); method @ColorInt public int getLeftEdgeEffectColor(); method public int getMaxScrollAmount(); method @ColorInt public int getRightEdgeEffectColor(); @@ -54352,6 +54450,7 @@ package android.widget { method public boolean isSmoothScrollingEnabled(); method public boolean pageScroll(int); method public void setEdgeEffectColor(@ColorInt int); + method public void setEdgeEffectType(int); method public void setFillViewport(boolean); method public void setLeftEdgeEffectColor(@ColorInt int); method public void setRightEdgeEffectColor(@ColorInt int); @@ -55071,6 +55170,7 @@ package android.widget { method public void setChronometerCountDown(@IdRes int, boolean); method public void setColor(@IdRes int, @NonNull String, @ColorRes int); method public void setColorInt(@IdRes int, @NonNull String, @ColorInt int, @ColorInt int); + method public void setColorStateList(@IdRes int, @NonNull String, @Nullable android.content.res.ColorStateList); method public void setColorStateList(@IdRes int, @NonNull String, @Nullable android.content.res.ColorStateList, @Nullable android.content.res.ColorStateList); method public void setColorStateList(@IdRes int, @NonNull String, @ColorRes int); method public void setCompoundButtonChecked(@IdRes int, boolean); @@ -55113,6 +55213,12 @@ package android.widget { method public void setTextViewText(@IdRes int, CharSequence); method public void setTextViewTextSize(@IdRes int, int, float); method public void setUri(@IdRes int, String, android.net.Uri); + method public void setViewLayoutHeight(@IdRes int, float, int); + method public void setViewLayoutHeightDimen(@IdRes int, @DimenRes int); + method public void setViewLayoutMargin(@IdRes int, int, float, int); + method public void setViewLayoutMarginDimen(@IdRes int, int, @DimenRes int); + method public void setViewLayoutWidth(@IdRes int, float, int); + method public void setViewLayoutWidthDimen(@IdRes int, @DimenRes int); method public void setViewOutlinePreferredRadius(@IdRes int, float, int); method public void setViewOutlinePreferredRadiusDimen(@IdRes int, @DimenRes int); method public void setViewPadding(@IdRes int, @Px int, @Px int, @Px int, @Px int); @@ -55123,6 +55229,12 @@ package android.widget { field @NonNull public static final android.os.Parcelable.Creator<android.widget.RemoteViews> CREATOR; field public static final String EXTRA_CHECKED = "android.widget.extra.CHECKED"; field public static final String EXTRA_SHARED_ELEMENT_BOUNDS = "android.widget.extra.SHARED_ELEMENT_BOUNDS"; + field public static final int MARGIN_BOTTOM = 3; // 0x3 + field public static final int MARGIN_END = 5; // 0x5 + field public static final int MARGIN_LEFT = 0; // 0x0 + field public static final int MARGIN_RIGHT = 2; // 0x2 + field public static final int MARGIN_START = 4; // 0x4 + field public static final int MARGIN_TOP = 1; // 0x1 } public static class RemoteViews.ActionException extends java.lang.RuntimeException { @@ -55192,6 +55304,7 @@ package android.widget { method public void fling(int); method public boolean fullScroll(int); method @ColorInt public int getBottomEdgeEffectColor(); + method public int getEdgeEffectType(); method public int getMaxScrollAmount(); method @ColorInt public int getTopEdgeEffectColor(); method public boolean isFillViewport(); @@ -55200,6 +55313,7 @@ package android.widget { method public void scrollToDescendant(@NonNull android.view.View); method public void setBottomEdgeEffectColor(@ColorInt int); method public void setEdgeEffectColor(@ColorInt int); + method public void setEdgeEffectType(int); method public void setFillViewport(boolean); method public void setSmoothScrollingEnabled(boolean); method public void setTopEdgeEffectColor(@ColorInt int); diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index de02d0bb7e98..dd9582fddd4a 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -135,7 +135,7 @@ package android.media.session { } public final class MediaSessionManager { - method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName, @NonNull android.os.UserHandle, @Nullable android.os.Handler); + method public void addOnActiveSessionsChangedListener(@Nullable android.content.ComponentName, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener); method public void dispatchMediaKeyEvent(@NonNull android.view.KeyEvent); method public void dispatchMediaKeyEvent(@NonNull android.view.KeyEvent, boolean); method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent); @@ -189,6 +189,10 @@ package android.net { field public static final int TRANSPORT_TEST = 7; // 0x7 } + public class NetworkWatchlistManager { + method @Nullable public byte[] getWatchlistConfigHash(); + } + public final class Proxy { method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo); } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index d79c11db9d09..05360141d9dd 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -884,7 +884,7 @@ package android.app.admin { public class DevicePolicyManager { method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner(); - method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser(); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"}) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwnerNameOnAnyUser(); method @Nullable public CharSequence getDeviceOwnerOrganizationName(); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getDeviceOwnerUser(); @@ -1826,7 +1826,7 @@ package android.bluetooth { method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public android.bluetooth.BufferConstraints getBufferConstraints(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getDynamicBufferSupport(); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setBufferMillis(int, int); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setBufferLengthMillis(int, int); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); field public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD = 1; // 0x1 field public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING = 2; // 0x2 @@ -1942,6 +1942,10 @@ package android.bluetooth { field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED"; } + public final class BluetoothMapClient implements android.bluetooth.BluetoothProfile { + method @RequiresPermission(android.Manifest.permission.SEND_SMS) public boolean sendMessage(@NonNull android.bluetooth.BluetoothDevice, @NonNull java.util.Collection<android.net.Uri>, @NonNull String, @Nullable android.app.PendingIntent, @Nullable android.app.PendingIntent); + } + public final class BluetoothPan implements android.bluetooth.BluetoothProfile { method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice); @@ -1974,6 +1978,7 @@ package android.bluetooth { field public static final int CONNECTION_POLICY_FORBIDDEN = 0; // 0x0 field public static final int CONNECTION_POLICY_UNKNOWN = -1; // 0xffffffff field public static final int HEADSET_CLIENT = 16; // 0x10 + field public static final int MAP_CLIENT = 18; // 0x12 field public static final int PAN = 5; // 0x5 field public static final int PBAP_CLIENT = 17; // 0x11 field @Deprecated public static final int PRIORITY_OFF = 0; // 0x0 @@ -2027,7 +2032,7 @@ package android.bluetooth { public final class BufferConstraints implements android.os.Parcelable { ctor public BufferConstraints(@NonNull java.util.List<android.bluetooth.BufferConstraint>); method public int describeContents(); - method @Nullable public android.bluetooth.BufferConstraint getCodec(int); + method @Nullable public android.bluetooth.BufferConstraint forCodec(int); method public void writeToParcel(@NonNull android.os.Parcel, int); field public static final int BUFFER_CODEC_MAX_NUM = 32; // 0x20 field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BufferConstraints> CREATOR; @@ -3398,42 +3403,12 @@ package android.hardware.hdmi { package android.hardware.lights { - public final class Light implements android.os.Parcelable { - method public int describeContents(); - method public int getId(); - method public int getOrdinal(); - method public int getType(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.Light> CREATOR; - } - public final class LightState implements android.os.Parcelable { - ctor public LightState(@ColorInt int); - method public int describeContents(); - method @ColorInt public int getColor(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.LightState> CREATOR; - } - - public final class LightsManager { - method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public java.util.List<android.hardware.lights.Light> getLights(); - method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public android.hardware.lights.LightsManager.LightsSession openSession(); - field public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8 - } - - public final class LightsManager.LightsSession implements java.lang.AutoCloseable { - method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void close(); - method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void requestLights(@NonNull android.hardware.lights.LightsRequest); - } - - public final class LightsRequest { + ctor @Deprecated public LightState(@ColorInt int); } - public static final class LightsRequest.Builder { - ctor public LightsRequest.Builder(); - method @NonNull public android.hardware.lights.LightsRequest build(); - method @NonNull public android.hardware.lights.LightsRequest.Builder clearLight(@NonNull android.hardware.lights.Light); - method @NonNull public android.hardware.lights.LightsRequest.Builder setLight(@NonNull android.hardware.lights.Light, @NonNull android.hardware.lights.LightState); + public abstract class LightsManager { + field @Deprecated public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8 } } @@ -4713,6 +4688,7 @@ package android.location { } public class LocationManager { + method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void addProviderRequestChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.ChangedListener); method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void flushGnssBatch(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>); method @Nullable public String getExtraLocationControllerPackage(); @@ -4727,7 +4703,7 @@ package android.location { method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String, @Nullable String); method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.UPDATE_APP_OPS_STATS}) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler); method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback); - method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean registerProviderRequestListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.Listener); + method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void removeProviderRequestChangedListener(@NonNull android.location.provider.ProviderRequest.ChangedListener); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent); @@ -4736,7 +4712,6 @@ package android.location { method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle); method @Deprecated @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setProviderEnabledForUser(@NonNull String, boolean, @NonNull android.os.UserHandle); method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean unregisterGnssBatchedLocationCallback(@NonNull android.location.BatchedLocationCallback); - method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void unregisterProviderRequestListener(@NonNull android.location.provider.ProviderRequest.Listener); } public final class LocationRequest implements android.os.Parcelable { @@ -4886,7 +4861,7 @@ package android.location.provider { method @NonNull public android.location.provider.ProviderRequest.Builder setWorkSource(@NonNull android.os.WorkSource); } - public static interface ProviderRequest.Listener { + public static interface ProviderRequest.ChangedListener { method public void onProviderRequestChanged(@NonNull String, @NonNull android.location.provider.ProviderRequest); } @@ -5097,7 +5072,7 @@ package android.media { } public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation { - method @RequiresPermission(android.Manifest.permission.BIND_IMS_SERVICE) public void setOnRtpRxNoticeListener(@NonNull android.content.Context, @NonNull android.media.MediaPlayer.OnRtpRxNoticeListener, @Nullable android.os.Handler); + method @RequiresPermission(android.Manifest.permission.BIND_IMS_SERVICE) public void setOnRtpRxNoticeListener(@NonNull android.content.Context, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaPlayer.OnRtpRxNoticeListener); } public static interface MediaPlayer.OnRtpRxNoticeListener { @@ -7941,6 +7916,28 @@ package android.net.util { } +package android.net.vcn { + + public class VcnManager { + method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void addVcnNetworkPolicyListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.vcn.VcnManager.VcnNetworkPolicyListener); + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.vcn.VcnNetworkPolicyResult applyVcnNetworkPolicy(@NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties); + method public void removeVcnNetworkPolicyListener(@NonNull android.net.vcn.VcnManager.VcnNetworkPolicyListener); + } + + public static interface VcnManager.VcnNetworkPolicyListener { + method public void onPolicyChanged(); + } + + public final class VcnNetworkPolicyResult implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities(); + method public boolean isTeardownRequested(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.vcn.VcnNetworkPolicyResult> CREATOR; + } + +} + package android.net.wifi { public final class WifiMigration { @@ -8288,7 +8285,7 @@ package android.os { public final class BugreportManager { method @RequiresPermission(android.Manifest.permission.DUMP) public void requestBugreport(@NonNull android.os.BugreportParams, @Nullable CharSequence, @Nullable CharSequence); - method @RequiresPermission(android.Manifest.permission.DUMP) public void startBugreport(@NonNull android.os.ParcelFileDescriptor, @Nullable android.os.ParcelFileDescriptor, @NonNull android.os.BugreportParams, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback); + method @RequiresPermission(android.Manifest.permission.DUMP) @WorkerThread public void startBugreport(@NonNull android.os.ParcelFileDescriptor, @Nullable android.os.ParcelFileDescriptor, @NonNull android.os.BugreportParams, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback); } public final class BugreportParams { @@ -9221,6 +9218,7 @@ package android.provider { field public static final String NAMESPACE_PERMISSIONS = "permissions"; field public static final String NAMESPACE_PRIVACY = "privacy"; field public static final String NAMESPACE_PROFCOLLECT_NATIVE_BOOT = "profcollect_native_boot"; + field public static final String NAMESPACE_REBOOT_READINESS = "reboot_readiness"; field public static final String NAMESPACE_ROLLBACK = "rollback"; field public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot"; field public static final String NAMESPACE_RUNTIME = "runtime"; @@ -10765,7 +10763,7 @@ package android.telecom { method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle(); method @Nullable public final String getTelecomCallId(); method @Deprecated public void onAudioStateChanged(android.telecom.AudioState); - method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean, @Nullable android.telecom.CallScreeningService.CallResponse, boolean); + method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(@NonNull android.telecom.Connection.CallFilteringCompletionInfo); method public final void resetConnectionTime(); method public void setCallDirection(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from=0) long); @@ -10781,6 +10779,16 @@ package android.telecom { field public static final int PROPERTY_REMOTELY_HOSTED = 2048; // 0x800 } + public static final class Connection.CallFilteringCompletionInfo implements android.os.Parcelable { + ctor public Connection.CallFilteringCompletionInfo(boolean, boolean, @Nullable android.telecom.CallScreeningService.CallResponse, @Nullable android.content.ComponentName); + method public int describeContents(); + method @Nullable public android.telecom.CallScreeningService.CallResponse getCallResponse(); + method @Nullable public android.content.ComponentName getCallScreeningComponent(); + method public boolean isBlocked(); + method public boolean isInContacts(); + field @NonNull public static final android.os.Parcelable.Creator<android.telecom.Connection.CallFilteringCompletionInfo> CREATOR; + } + public final class ConnectionRequest implements android.os.Parcelable { method @Nullable public String getTelecomCallId(); } @@ -10942,7 +10950,7 @@ package android.telecom { } public final class RemoteConnection { - method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean, @Nullable android.telecom.CallScreeningService.CallResponse, boolean); + method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(@NonNull android.telecom.Connection.CallFilteringCompletionInfo); method @Deprecated public void setAudioState(android.telecom.AudioState); } @@ -12253,6 +12261,7 @@ package android.telephony.data { method public long getRetryDurationMillis(); method @Nullable public android.telephony.data.SliceInfo getSliceInfo(); method @Deprecated public int getSuggestedRetryTime(); + method @NonNull public java.util.List<android.telephony.data.TrafficDescriptor> getTrafficDescriptors(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR; field public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 1; // 0x1 @@ -12288,6 +12297,7 @@ package android.telephony.data { method @NonNull public android.telephony.data.DataCallResponse.Builder setRetryDurationMillis(long); method @NonNull public android.telephony.data.DataCallResponse.Builder setSliceInfo(@Nullable android.telephony.data.SliceInfo); method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setSuggestedRetryTime(int); + method @NonNull public android.telephony.data.DataCallResponse.Builder setTrafficDescriptors(@NonNull java.util.List<android.telephony.data.TrafficDescriptor>); } public final class DataProfile implements android.os.Parcelable { @@ -12358,7 +12368,7 @@ package android.telephony.data { method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback); method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback); method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @NonNull android.telephony.data.DataServiceCallback); - method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @IntRange(from=0, to=15) int, @Nullable android.telephony.data.SliceInfo, @NonNull android.telephony.data.DataServiceCallback); + method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @IntRange(from=0, to=15) int, @Nullable android.telephony.data.SliceInfo, @Nullable android.telephony.data.TrafficDescriptor, boolean, @NonNull android.telephony.data.DataServiceCallback); } public class DataServiceCallback { @@ -12457,6 +12467,15 @@ package android.telephony.data { method @NonNull public android.telephony.data.ThrottleStatus.Builder setTransportType(int); } + public final class TrafficDescriptor implements android.os.Parcelable { + ctor public TrafficDescriptor(@Nullable String, @Nullable String); + method public int describeContents(); + method @Nullable public String getDnn(); + method @Nullable public String getOsAppId(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.TrafficDescriptor> CREATOR; + } + } package android.telephony.euicc { @@ -14276,21 +14295,10 @@ package android.uwb { public final class UwbManager { method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public long elapsedRealtimeResolutionNanos(); - method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getAngleOfArrivalSupport(); - method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getMaxRemoteDevicesPerInitiatorSession(); - method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getMaxRemoteDevicesPerResponderSession(); - method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getMaxSimultaneousSessions(); method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public android.os.PersistableBundle getSpecificationInfo(); - method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public java.util.List<java.lang.Integer> getSupportedChannelNumbers(); - method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public java.util.Set<java.lang.Integer> getSupportedPreambleCodeIndices(); - method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public boolean isRangingSupported(); method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public AutoCloseable openRangingSession(@NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.uwb.RangingSession.Callback); method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void registerAdapterStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.uwb.UwbManager.AdapterStateCallback); method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void unregisterAdapterStateCallback(@NonNull android.uwb.UwbManager.AdapterStateCallback); - field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D = 2; // 0x2 - field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL = 3; // 0x3 - field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL = 4; // 0x4 - field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE = 1; // 0x1 } public static interface UwbManager.AdapterStateCallback { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index d7b43c0bc48e..835c49fccf7d 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -10,9 +10,11 @@ package android { field public static final String BROADCAST_CLOSE_SYSTEM_DIALOGS = "android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"; field public static final String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE"; field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA"; + field public static final String CLEAR_FREEZE_PERIOD = "android.permission.CLEAR_FREEZE_PERIOD"; field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"; field public static final String CONTROL_DEVICE_LIGHTS = "android.permission.CONTROL_DEVICE_LIGHTS"; field public static final String CONTROL_DEVICE_STATE = "android.permission.CONTROL_DEVICE_STATE"; + field public static final String FORCE_DEVICE_POLICY_MANAGER_LOGS = "android.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS"; field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES"; field public static final String KEEP_UNINSTALLED_PACKAGES = "android.permission.KEEP_UNINSTALLED_PACKAGES"; field @Deprecated public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS"; @@ -91,6 +93,8 @@ package android.app { method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener); method public void alwaysShowUnsupportedCompileSdkWarning(android.content.ComponentName); method public long getTotalRam(); + method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidProcessCapabilities(int); + method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidProcessState(int); method public void holdLock(android.os.IBinder, int); method public static boolean isHighEndGfx(); method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener); @@ -100,13 +104,16 @@ package android.app { method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String); field public static final long DROP_CLOSE_SYSTEM_DIALOGS = 174664120L; // 0xa6929b8L field public static final long LOCK_DOWN_CLOSE_SYSTEM_DIALOGS = 174664365L; // 0xa692aadL - field public static final int PROCESS_CAPABILITY_ALL = 7; // 0x7 + field public static final int PROCESS_CAPABILITY_ALL = 15; // 0xf field public static final int PROCESS_CAPABILITY_ALL_EXPLICIT = 1; // 0x1 field public static final int PROCESS_CAPABILITY_ALL_IMPLICIT = 6; // 0x6 field public static final int PROCESS_CAPABILITY_FOREGROUND_CAMERA = 2; // 0x2 field public static final int PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1; // 0x1 field public static final int PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 4; // 0x4 + field public static final int PROCESS_CAPABILITY_NETWORK = 8; // 0x8 field public static final int PROCESS_CAPABILITY_NONE = 0; // 0x0 + field public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4; // 0x4 + field public static final int PROCESS_STATE_TOP = 2; // 0x2 } public static class ActivityManager.RunningAppProcessInfo implements android.os.Parcelable { @@ -384,7 +391,11 @@ package android.app.admin { public class DevicePolicyManager { method public int checkProvisioningPreCondition(@Nullable String, @NonNull String); + method @RequiresPermission(android.Manifest.permission.CLEAR_FREEZE_PERIOD) public void clearSystemUpdatePolicyFreezePeriodRecord(); method @Nullable public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException; + method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs(); + method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void forceRemoveActiveAdmin(@NonNull android.content.ComponentName, int); + method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs(); method public void forceUpdateUserSetupComplete(); method public long getLastBugReportRequestTime(); method public long getLastNetworkLogRetrievalTime(); @@ -392,10 +403,13 @@ package android.app.admin { method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(@NonNull android.os.UserHandle); method public boolean isCurrentInputMethodSetByOwner(); method public boolean isFactoryResetProtectionPolicySupported(); + method @RequiresPermission(anyOf={"android.permission.MARK_DEVICE_ORGANIZATION_OWNED", "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName); method @NonNull public static String operationSafetyReasonToString(int); method @NonNull public static String operationToString(int); method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException; method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void resetDefaultCrossProfileIntentFilters(int); + method @RequiresPermission(allOf={"android.permission.MANAGE_DEVICE_ADMINS", android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setActiveAdmin(@NonNull android.content.ComponentName, boolean, int); + method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public boolean setDeviceOwner(@NonNull android.content.ComponentName, @Nullable String, int); method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, int); field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED"; field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6 @@ -580,6 +594,14 @@ package android.app.usage { } +package android.appwidget { + + public class AppWidgetManager { + method public void setBindAppWidgetPermission(@NonNull String, int, boolean); + } + +} + package android.bluetooth { public final class BluetoothClass implements android.os.Parcelable { @@ -1001,14 +1023,6 @@ package android.hardware.input { } -package android.hardware.lights { - - public final class LightsManager { - method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public android.hardware.lights.LightState getLightState(@NonNull android.hardware.lights.Light); - } - -} - package android.hardware.soundtrigger { public class KeyphraseEnrollmentInfo { @@ -2556,6 +2570,10 @@ package android.view.inputmethod { method @NonNull public static android.view.inputmethod.InlineSuggestionsResponse newInlineSuggestionsResponse(@NonNull java.util.List<android.view.inputmethod.InlineSuggestion>); } + public final class InputMethodInfo implements android.os.Parcelable { + ctor public InputMethodInfo(@NonNull String, @NonNull String, @NonNull CharSequence, @NonNull String, int); + } + public final class InputMethodManager { method public int getDisplayId(); method public boolean hasActiveInputConnection(@Nullable android.view.View); diff --git a/core/java/android/app/ActivityManager.aidl b/core/java/android/app/ActivityManager.aidl index 341393c0d969..eb7cac076822 100644 --- a/core/java/android/app/ActivityManager.aidl +++ b/core/java/android/app/ActivityManager.aidl @@ -17,6 +17,7 @@ package android.app; parcelable ActivityManager.MemoryInfo; +parcelable ActivityManager.PendingIntentInfo; parcelable ActivityManager.ProcessErrorStateInfo; parcelable ActivityManager.RecentTaskInfo; parcelable ActivityManager.TaskDescription; diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index e2426d116319..f905ec86aab7 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -511,6 +511,7 @@ public class ActivityManager { /** @hide Process is hosting the current top activities. Note that this covers * all activities that are visible to the user. */ @UnsupportedAppUsage + @TestApi public static final int PROCESS_STATE_TOP = ProcessStateEnum.TOP; /** @hide Process is bound to a TOP app. */ @@ -518,6 +519,7 @@ public class ActivityManager { /** @hide Process is hosting a foreground service. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @TestApi public static final int PROCESS_STATE_FOREGROUND_SERVICE = ProcessStateEnum.FOREGROUND_SERVICE; /** @hide Process is hosting a foreground service due to a system binding. */ @@ -616,11 +618,16 @@ public class ActivityManager { @TestApi public static final int PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 1 << 2; + /** @hide Process can access network despite any power saving resrictions */ + @TestApi + public static final int PROCESS_CAPABILITY_NETWORK = 1 << 3; + /** @hide all capabilities, the ORing of all flags in {@link ProcessCapability}*/ @TestApi public static final int PROCESS_CAPABILITY_ALL = PROCESS_CAPABILITY_FOREGROUND_LOCATION | PROCESS_CAPABILITY_FOREGROUND_CAMERA - | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE; + | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE + | PROCESS_CAPABILITY_NETWORK; /** * All explicit capabilities. These are capabilities that need to be specified from manifest * file. @@ -646,6 +653,15 @@ public class ActivityManager { pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_LOCATION) != 0 ? 'L' : '-'); pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0 ? 'C' : '-'); pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0 ? 'M' : '-'); + pw.print((caps & PROCESS_CAPABILITY_NETWORK) != 0 ? 'N' : '-'); + } + + /** @hide */ + public static void printCapabilitiesSummary(StringBuilder sb, @ProcessCapability int caps) { + sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_LOCATION) != 0 ? 'L' : '-'); + sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0 ? 'C' : '-'); + sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0 ? 'M' : '-'); + sb.append((caps & PROCESS_CAPABILITY_NETWORK) != 0 ? 'N' : '-'); } /** @@ -656,13 +672,21 @@ public class ActivityManager { printCapabilitiesSummary(pw, caps); final int remain = caps & ~(PROCESS_CAPABILITY_FOREGROUND_LOCATION | PROCESS_CAPABILITY_FOREGROUND_CAMERA - | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE); + | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE + | PROCESS_CAPABILITY_NETWORK); if (remain != 0) { pw.print('+'); pw.print(remain); } } + /** @hide */ + public static String getCapabilitiesSummary(@ProcessCapability int caps) { + final StringBuilder sb = new StringBuilder(); + printCapabilitiesSummary(sb, caps); + return sb.toString(); + } + // NOTE: If PROCESS_STATEs are added, then new fields must be added // to frameworks/base/core/proto/android/app/enums.proto and the following method must // be updated to correctly map between them. @@ -3411,6 +3435,36 @@ public class ActivityManager { } /** + * Returns the process state of this uid. + * + * @hide + */ + @TestApi + @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS) + public int getUidProcessState(int uid) { + try { + return getService().getUidProcessState(uid, mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns the process capability of this uid. + * + * @hide + */ + @TestApi + @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS) + public @ProcessCapability int getUidProcessCapabilities(int uid) { + try { + return getService().getUidProcessCapabilities(uid, mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Return the importance of a given package name, based on the processes that are * currently running. The return value is one of the importance constants defined * in {@link RunningAppProcessInfo}, giving you the highest importance of all the @@ -4485,6 +4539,80 @@ public class ActivityManager { } } + /** @hide */ + public static String procStateToString(int procState) { + final String procStateStr; + switch (procState) { + case ActivityManager.PROCESS_STATE_PERSISTENT: + procStateStr = "PER "; + break; + case ActivityManager.PROCESS_STATE_PERSISTENT_UI: + procStateStr = "PERU"; + break; + case ActivityManager.PROCESS_STATE_TOP: + procStateStr = "TOP "; + break; + case ActivityManager.PROCESS_STATE_BOUND_TOP: + procStateStr = "BTOP"; + break; + case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE: + procStateStr = "FGS "; + break; + case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE: + procStateStr = "BFGS"; + break; + case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND: + procStateStr = "IMPF"; + break; + case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND: + procStateStr = "IMPB"; + break; + case ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND: + procStateStr = "TRNB"; + break; + case ActivityManager.PROCESS_STATE_BACKUP: + procStateStr = "BKUP"; + break; + case ActivityManager.PROCESS_STATE_SERVICE: + procStateStr = "SVC "; + break; + case ActivityManager.PROCESS_STATE_RECEIVER: + procStateStr = "RCVR"; + break; + case ActivityManager.PROCESS_STATE_TOP_SLEEPING: + procStateStr = "TPSL"; + break; + case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT: + procStateStr = "HVY "; + break; + case ActivityManager.PROCESS_STATE_HOME: + procStateStr = "HOME"; + break; + case ActivityManager.PROCESS_STATE_LAST_ACTIVITY: + procStateStr = "LAST"; + break; + case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: + procStateStr = "CAC "; + break; + case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: + procStateStr = "CACC"; + break; + case ActivityManager.PROCESS_STATE_CACHED_RECENT: + procStateStr = "CRE "; + break; + case ActivityManager.PROCESS_STATE_CACHED_EMPTY: + procStateStr = "CEM "; + break; + case ActivityManager.PROCESS_STATE_NONEXISTENT: + procStateStr = "NONE"; + break; + default: + procStateStr = "??"; + break; + } + return procStateStr; + } + /** * The AppTask allows you to manage your own application's tasks. * See {@link android.app.ActivityManager#getAppTasks()} @@ -4681,4 +4809,71 @@ public class ActivityManager { throw e.rethrowFromSystemServer(); } } + + /** + * A subset of immutable pending intent information suitable for caching on the client side. + * + * @hide + */ + public static final class PendingIntentInfo implements Parcelable { + + private final String mCreatorPackage; + private final int mCreatorUid; + private final boolean mImmutable; + private final int mIntentSenderType; + + public PendingIntentInfo(String creatorPackage, int creatorUid, boolean immutable, + int intentSenderType) { + mCreatorPackage = creatorPackage; + mCreatorUid = creatorUid; + mImmutable = immutable; + mIntentSenderType = intentSenderType; + } + + public String getCreatorPackage() { + return mCreatorPackage; + } + + public int getCreatorUid() { + return mCreatorUid; + } + + public boolean isImmutable() { + return mImmutable; + } + + public int getIntentSenderType() { + return mIntentSenderType; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel parcel, int flags) { + parcel.writeString(mCreatorPackage); + parcel.writeInt(mCreatorUid); + parcel.writeBoolean(mImmutable); + parcel.writeInt(mIntentSenderType); + } + + public static final @NonNull Creator<PendingIntentInfo> CREATOR = + new Creator<PendingIntentInfo>() { + @Override + public PendingIntentInfo createFromParcel(Parcel in) { + return new PendingIntentInfo( + /* creatorPackage= */ in.readString(), + /* creatorUid= */ in.readInt(), + /* immutable= */ in.readBoolean(), + /* intentSenderType= */ in.readInt()); + } + + @Override + public PendingIntentInfo[] newArray(int size) { + return new PendingIntentInfo[size]; + } + }; + } } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index b8735c731817..2f3b50b17d51 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -2551,9 +2551,9 @@ public class AppOpsManager { false, // READ_MEDIA_AUDIO false, // WRITE_MEDIA_AUDIO false, // READ_MEDIA_VIDEO - false, // WRITE_MEDIA_VIDEO + true, // WRITE_MEDIA_VIDEO false, // READ_MEDIA_IMAGES - false, // WRITE_MEDIA_IMAGES + true, // WRITE_MEDIA_IMAGES true, // LEGACY_STORAGE false, // ACCESS_ACCESSIBILITY false, // READ_DEVICE_IDENTIFIERS diff --git a/core/java/android/app/BackgroundServiceStartNotAllowedException.java b/core/java/android/app/BackgroundServiceStartNotAllowedException.java new file mode 100644 index 000000000000..f6361b52bf9d --- /dev/null +++ b/core/java/android/app/BackgroundServiceStartNotAllowedException.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Exception thrown when an app tries to start a background {@link Service} when it's not allowed to + * do so. + */ +public final class BackgroundServiceStartNotAllowedException + extends ServiceStartNotAllowedException implements Parcelable { + /** + * Constructor. + */ + public BackgroundServiceStartNotAllowedException(@NonNull String message) { + super(message); + } + + BackgroundServiceStartNotAllowedException(@NonNull Parcel source) { + super(source.readString()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(getMessage()); + } + + public static final @NonNull Creator<android.app.BackgroundServiceStartNotAllowedException> + CREATOR = new Creator<android.app.BackgroundServiceStartNotAllowedException>() { + @NonNull + public android.app.BackgroundServiceStartNotAllowedException createFromParcel( + Parcel source) { + return new android.app.BackgroundServiceStartNotAllowedException(source); + } + + @NonNull + public android.app.BackgroundServiceStartNotAllowedException[] newArray(int size) { + return new android.app.BackgroundServiceStartNotAllowedException[size]; + } + }; +} diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 85fb543a3967..bc798136f2e3 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1796,7 +1796,7 @@ class ContextImpl extends Context { "Unable to start service " + service + ": " + cn.getClassName()); } else if (cn.getPackageName().equals("?")) { - throw new IllegalStateException( + throw ServiceStartNotAllowedException.newInstance(requireForeground, "Not allowed to start service " + service + ": " + cn.getClassName()); } } diff --git a/core/java/android/app/ForegroundServiceStartNotAllowedException.java b/core/java/android/app/ForegroundServiceStartNotAllowedException.java new file mode 100644 index 000000000000..41eeada2df6b --- /dev/null +++ b/core/java/android/app/ForegroundServiceStartNotAllowedException.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Exception thrown when an app tries to start a foreground {@link Service} when it's not allowed to + * do so. + */ +public final class ForegroundServiceStartNotAllowedException + extends ServiceStartNotAllowedException implements Parcelable { + /** + * Constructor. + */ + public ForegroundServiceStartNotAllowedException(@NonNull String message) { + super(message); + } + + ForegroundServiceStartNotAllowedException(@NonNull Parcel source) { + super(source.readString()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(getMessage()); + } + + public static final @NonNull Creator<android.app.ForegroundServiceStartNotAllowedException> + CREATOR = new Creator<android.app.ForegroundServiceStartNotAllowedException>() { + @NonNull + public android.app.ForegroundServiceStartNotAllowedException createFromParcel( + Parcel source) { + return new android.app.ForegroundServiceStartNotAllowedException(source); + } + + @NonNull + public android.app.ForegroundServiceStartNotAllowedException[] newArray(int size) { + return new android.app.ForegroundServiceStartNotAllowedException[size]; + } + }; +} diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 4ad13e1932dd..2e684b1d3ef7 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -17,6 +17,7 @@ package android.app; import android.app.ActivityManager; +import android.app.ActivityManager.PendingIntentInfo; import android.app.ActivityTaskManager; import android.app.ApplicationErrorReport; import android.app.ApplicationExitInfo; @@ -248,7 +249,7 @@ interface IActivityManager { in IBinder token, in String resultWho, int requestCode, in Intent[] intents, in String[] resolvedTypes, int flags, in Bundle options, int userId); void cancelIntentSender(in IIntentSender sender); - String getPackageForIntentSender(in IIntentSender sender); + ActivityManager.PendingIntentInfo getInfoForIntentSender(in IIntentSender sender); void registerIntentSenderCancelListener(in IIntentSender sender, in IResultReceiver receiver); void unregisterIntentSenderCancelListener(in IIntentSender sender, in IResultReceiver receiver); void enterSafeMode(); @@ -293,7 +294,6 @@ interface IActivityManager { int operationType); void backupAgentCreated(in String packageName, in IBinder agent, int userId); void unbindBackupAgent(in ApplicationInfo appInfo); - int getUidForIntentSender(in IIntentSender sender); int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll, boolean requireFull, in String name, in String callerPackage); void addPackageDependency(in String packageName); @@ -345,7 +345,6 @@ interface IActivityManager { @UnsupportedAppUsage void unregisterProcessObserver(in IProcessObserver observer); boolean isIntentSenderTargetedToPackage(in IIntentSender sender); - boolean isIntentSenderImmutable(in IIntentSender sender); @UnsupportedAppUsage void updatePersistentConfiguration(in Configuration values); void updatePersistentConfigurationWithAttribution(in Configuration values, @@ -375,8 +374,6 @@ interface IActivityManager { void unstableProviderDied(in IBinder connection); @UnsupportedAppUsage boolean isIntentSenderAnActivity(in IIntentSender sender); - boolean isIntentSenderAForegroundService(in IIntentSender sender); - boolean isIntentSenderABroadcast(in IIntentSender sender); /** @deprecated Use {@link startActivityAsUserWithFeature} instead */ @UnsupportedAppUsage(maxTargetSdk=29, publicAlternatives="Use {@code android.content.Context#createContextAsUser(android.os.UserHandle, int)} and {@link android.content.Context#startActivity(android.content.Intent)} instead") int startActivityAsUser(in IApplicationThread caller, in String callingPackage, @@ -711,5 +708,5 @@ interface IActivityManager { /** Called by PendingIntent.queryIntentComponents() */ List<ResolveInfo> queryIntentComponentsForIntentSender(in IIntentSender sender, int matchFlags); - boolean isIntentSenderAService(in IIntentSender sender); + int getUidProcessCapabilities(int uid, in String callingPackage); } diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index b1c39d39d414..7bb5d9a8d387 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -60,9 +60,9 @@ oneway interface ITaskStackListener { void onActivityForcedResizable(String packageName, int taskId, int reason); /** - * Called when we launched an activity that dismissed the docked stack. + * Called when we launched an activity that dismissed the docked task. */ - void onActivityDismissingDockedStack(); + void onActivityDismissingDockedTask(); /** * Called when an activity was requested to be launched on a secondary display but was not diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 899cdb5eb572..f7304fbee6d6 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1371,6 +1371,18 @@ public class Notification implements Parcelable public static final String EXTRA_HANG_UP_INTENT = "android.hangUpIntent"; /** + * {@link #extras} key: the color used as a hint for the Answer action button of a + * {@link android.app.Notification.CallStyle} notification. This extra is a {@link ColorInt}. + */ + public static final String EXTRA_ANSWER_COLOR = "android.answerColor"; + + /** + * {@link #extras} key: the color used as a hint for the Decline or Hang Up action button of a + * {@link android.app.Notification.CallStyle} notification. This extra is a {@link ColorInt}. + */ + public static final String EXTRA_DECLINE_COLOR = "android.declineColor"; + + /** * {@link #extras} key: whether the notification should be colorized as * supplied to {@link Builder#setColorized(boolean)}. */ @@ -5444,6 +5456,11 @@ public class Notification implements Parcelable return p.allowColorization && mN.isColorized(); } + private boolean isCallActionColorCustomizable(StandardTemplateParams p) { + return isColorized(p) && mContext.getResources().getBoolean( + R.bool.config_callNotificationActionColorsRequireColorized); + } + private void bindSmallIcon(RemoteViews contentView, StandardTemplateParams p) { if (mN.mSmallIcon == null && mN.icon != 0) { mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon); @@ -9066,6 +9083,8 @@ public class Notification implements Parcelable private PendingIntent mAnswerIntent; private PendingIntent mDeclineIntent; private PendingIntent mHangUpIntent; + private Integer mAnswerButtonColor; + private Integer mDeclineButtonColor; private Icon mVerificationIcon; private CharSequence mVerificationText; @@ -9176,6 +9195,28 @@ public class Notification implements Parcelable } /** + * Optional color to be used as a hint for the Answer action button's color. + * The system may change this color to ensure sufficient contrast with the background. + * The system may choose to disregard this hint if the notification is not colorized. + */ + @NonNull + public CallStyle setAnswerButtonColorHint(@ColorInt int color) { + mAnswerButtonColor = color; + return this; + } + + /** + * Optional color to be used as a hint for the Decline or Hang Up action button's color. + * The system may change this color to ensure sufficient contrast with the background. + * The system may choose to disregard this hint if the notification is not colorized. + */ + @NonNull + public CallStyle setDeclineButtonColorHint(@ColorInt int color) { + mDeclineButtonColor = color; + return this; + } + + /** * @hide */ public boolean displayCustomViewInline() { @@ -9234,40 +9275,47 @@ public class Notification implements Parcelable } @NonNull - private Action makeNegativeAction() { + private Action makeNegativeAction(@NonNull StandardTemplateParams p) { if (mDeclineIntent == null) { - return makeAction(R.drawable.ic_call_decline, + return makeAction(p, R.drawable.ic_call_decline, R.string.call_notification_hang_up_action, - R.color.call_notification_decline_color, mHangUpIntent); + mDeclineButtonColor, R.color.call_notification_decline_color, + mHangUpIntent); } else { - return makeAction(R.drawable.ic_call_decline, + return makeAction(p, R.drawable.ic_call_decline, R.string.call_notification_decline_action, - R.color.call_notification_decline_color, mDeclineIntent); + mDeclineButtonColor, R.color.call_notification_decline_color, + mDeclineIntent); } } @Nullable - private Action makeAnswerAction() { - return mAnswerIntent == null ? null : makeAction(R.drawable.ic_call_answer, + private Action makeAnswerAction(@NonNull StandardTemplateParams p) { + return mAnswerIntent == null ? null : makeAction(p, R.drawable.ic_call_answer, R.string.call_notification_answer_action, - R.color.call_notification_answer_color, mAnswerIntent); + mAnswerButtonColor, R.color.call_notification_answer_color, + mAnswerIntent); } @NonNull - private Action makeAction(@DrawableRes int icon, @StringRes int title, - @ColorRes int colorRes, PendingIntent intent) { + private Action makeAction(@NonNull StandardTemplateParams p, + @DrawableRes int icon, @StringRes int title, + @ColorInt Integer colorInt, @ColorRes int defaultColorRes, PendingIntent intent) { + if (colorInt == null || !mBuilder.isCallActionColorCustomizable(p)) { + colorInt = mBuilder.mContext.getColor(defaultColorRes); + } Action action = new Action.Builder(Icon.createWithResource("", icon), new SpannableStringBuilder().append(mBuilder.mContext.getString(title), - new ForegroundColorSpan(mBuilder.mContext.getColor(colorRes)), + new ForegroundColorSpan(colorInt), SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE), intent).build(); action.getExtras().putBoolean(KEY_ACTION_PRIORITY, true); return action; } - private ArrayList<Action> makeActionsList() { - final Action negativeAction = makeNegativeAction(); - final Action answerAction = makeAnswerAction(); + private ArrayList<Action> makeActionsList(@NonNull StandardTemplateParams p) { + final Action negativeAction = makeNegativeAction(p); + final Action answerAction = makeAnswerAction(p); ArrayList<Action> actions = new ArrayList<>(MAX_ACTION_BUTTONS); final Action lastAction; @@ -9356,7 +9404,7 @@ public class Notification implements Parcelable // Create the buttons for the generated actions list. int i = 0; - for (Action action : makeActionsList()) { + for (Action action : makeActionsList(p)) { final RemoteViews button = mBuilder.generateActionButton(action, emphasizedMode, p); if (i > 0) { // Clear start margin from non-first buttons to reduce the gap between buttons. @@ -9421,6 +9469,12 @@ public class Notification implements Parcelable if (mHangUpIntent != null) { extras.putParcelable(EXTRA_HANG_UP_INTENT, mHangUpIntent); } + if (mAnswerButtonColor != null) { + extras.putInt(EXTRA_ANSWER_COLOR, mAnswerButtonColor); + } + if (mDeclineButtonColor != null) { + extras.putInt(EXTRA_DECLINE_COLOR, mDeclineButtonColor); + } fixTitleAndTextExtras(extras); } @@ -9447,6 +9501,10 @@ public class Notification implements Parcelable mAnswerIntent = extras.getParcelable(EXTRA_ANSWER_INTENT); mDeclineIntent = extras.getParcelable(EXTRA_DECLINE_INTENT); mHangUpIntent = extras.getParcelable(EXTRA_HANG_UP_INTENT); + mAnswerButtonColor = extras.containsKey(EXTRA_ANSWER_COLOR) + ? extras.getInt(EXTRA_ANSWER_COLOR) : null; + mDeclineButtonColor = extras.containsKey(EXTRA_DECLINE_COLOR) + ? extras.getInt(EXTRA_DECLINE_COLOR) : null; } /** diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 671315f37b20..2f06bdce9087 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -16,6 +16,11 @@ package android.app; +import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY; +import static android.app.ActivityManager.INTENT_SENDER_BROADCAST; +import static android.app.ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE; +import static android.app.ActivityManager.INTENT_SENDER_SERVICE; + import android.Manifest.permission; import android.annotation.IntDef; import android.annotation.NonNull; @@ -25,6 +30,7 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemApi.Client; import android.annotation.TestApi; +import android.app.ActivityManager.PendingIntentInfo; import android.compat.Compatibility; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; @@ -122,6 +128,9 @@ public final class PendingIntent implements Parcelable { private IBinder mWhitelistToken; private ArraySet<CancelListener> mCancelListeners; + // cached pending intent information + private @Nullable PendingIntentInfo mCachedInfo; + /** * It is now required to specify either {@link #FLAG_IMMUTABLE} * or {@link #FLAG_MUTABLE} when creating a PendingIntent. @@ -463,7 +472,7 @@ public final class PendingIntent implements Parcelable { intent.prepareToLeaveProcess(context); IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature( - ActivityManager.INTENT_SENDER_ACTIVITY, packageName, + INTENT_SENDER_ACTIVITY, packageName, context.getAttributionTag(), null, null, requestCode, new Intent[] { intent }, resolvedType != null ? new String[] { resolvedType } : null, flags, options, user.getIdentifier()); @@ -596,7 +605,7 @@ public final class PendingIntent implements Parcelable { try { IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature( - ActivityManager.INTENT_SENDER_ACTIVITY, packageName, + INTENT_SENDER_ACTIVITY, packageName, context.getAttributionTag(), null, null, requestCode, intents, resolvedTypes, flags, options, user.getIdentifier()); return target != null ? new PendingIntent(target) : null; @@ -650,7 +659,7 @@ public final class PendingIntent implements Parcelable { intent.prepareToLeaveProcess(context); IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature( - ActivityManager.INTENT_SENDER_BROADCAST, packageName, + INTENT_SENDER_BROADCAST, packageName, context.getAttributionTag(), null, null, requestCode, new Intent[] { intent }, resolvedType != null ? new String[] { resolvedType } : null, flags, null, userHandle.getIdentifier()); @@ -687,7 +696,7 @@ public final class PendingIntent implements Parcelable { public static PendingIntent getService(Context context, int requestCode, @NonNull Intent intent, @Flags int flags) { return buildServicePendingIntent(context, requestCode, intent, flags, - ActivityManager.INTENT_SENDER_SERVICE); + INTENT_SENDER_SERVICE); } /** @@ -717,7 +726,7 @@ public final class PendingIntent implements Parcelable { public static PendingIntent getForegroundService(Context context, int requestCode, @NonNull Intent intent, @Flags int flags) { return buildServicePendingIntent(context, requestCode, intent, flags, - ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE); + INTENT_SENDER_FOREGROUND_SERVICE); } private static PendingIntent buildServicePendingIntent(Context context, int requestCode, @@ -1001,12 +1010,7 @@ public final class PendingIntent implements Parcelable { */ @Deprecated public String getTargetPackage() { - try { - return ActivityManager.getService() - .getPackageForIntentSender(mTarget); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return getCreatorPackage(); } /** @@ -1029,12 +1033,7 @@ public final class PendingIntent implements Parcelable { */ @Nullable public String getCreatorPackage() { - try { - return ActivityManager.getService() - .getPackageForIntentSender(mTarget); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return getCachedInfo().getCreatorPackage(); } /** @@ -1056,12 +1055,7 @@ public final class PendingIntent implements Parcelable { * none associated with it. */ public int getCreatorUid() { - try { - return ActivityManager.getService() - .getUidForIntentSender(mTarget); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return getCachedInfo().getCreatorUid(); } /** @@ -1154,13 +1148,8 @@ public final class PendingIntent implements Parcelable { */ @Nullable public UserHandle getCreatorUserHandle() { - try { - int uid = ActivityManager.getService() - .getUidForIntentSender(mTarget); - return uid > 0 ? new UserHandle(UserHandle.getUserId(uid)) : null; - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + int uid = getCachedInfo().getCreatorUid(); + return uid > 0 ? new UserHandle(UserHandle.getUserId(uid)) : null; } /** @@ -1180,12 +1169,7 @@ public final class PendingIntent implements Parcelable { * Check if this PendingIntent is marked with {@link #FLAG_IMMUTABLE}. */ public boolean isImmutable() { - try { - return ActivityManager.getService() - .isIntentSenderImmutable(mTarget); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return getCachedInfo().isImmutable(); } /** @@ -1193,48 +1177,28 @@ public final class PendingIntent implements Parcelable { * {@link #getActivity} or {@link #getActivities}. */ public boolean isActivity() { - try { - return ActivityManager.getService() - .isIntentSenderAnActivity(mTarget); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return getCachedInfo().getIntentSenderType() == INTENT_SENDER_ACTIVITY; } /** * @return TRUE if this {@link PendingIntent} was created with {@link #getForegroundService}. */ public boolean isForegroundService() { - try { - return ActivityManager.getService() - .isIntentSenderAForegroundService(mTarget); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return getCachedInfo().getIntentSenderType() == INTENT_SENDER_FOREGROUND_SERVICE; } /** * @return TRUE if this {@link PendingIntent} was created with {@link #getService}. */ public boolean isService() { - try { - return ActivityManager.getService() - .isIntentSenderAService(mTarget); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return getCachedInfo().getIntentSenderType() == INTENT_SENDER_SERVICE; } /** * @return TRUE if this {@link PendingIntent} was created with {@link #getBroadcast}. */ public boolean isBroadcast() { - try { - return ActivityManager.getService() - .isIntentSenderABroadcast(mTarget); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return getCachedInfo().getIntentSenderType() == INTENT_SENDER_BROADCAST; } /** @@ -1433,4 +1397,16 @@ public final class PendingIntent implements Parcelable { */ void onCancelled(PendingIntent intent); } + + private PendingIntentInfo getCachedInfo() { + if (mCachedInfo == null) { + try { + mCachedInfo = ActivityManager.getService().getInfoForIntentSender(mTarget); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + return mCachedInfo; + } } diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index 3798de921dc7..2ceea7f1a6a8 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -697,7 +697,8 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * service element of manifest file. The value of attribute * {@link android.R.attr#foregroundServiceType} can be multiple flags ORed together.</p> * - * @throws IllegalStateException If the app targeting API is + * @throws ForegroundServiceStartNotAllowedException + * If the app targeting API is * {@link android.os.Build.VERSION_CODES#S} or later, and the service is restricted from * becoming foreground service due to background restriction. * @@ -738,8 +739,14 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * @param notification The Notification to be displayed. * @param foregroundServiceType must be a subset flags of manifest attribute * {@link android.R.attr#foregroundServiceType} flags. + * * @throws IllegalArgumentException if param foregroundServiceType is not subset of manifest * attribute {@link android.R.attr#foregroundServiceType}. + * @throws ForegroundServiceStartNotAllowedException + * If the app targeting API is + * {@link android.os.Build.VERSION_CODES#S} or later, and the service is restricted from + * becoming foreground service due to background restriction. + * * @see android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST */ public final void startForeground(int id, @NonNull Notification notification, diff --git a/core/java/android/app/ServiceStartNotAllowedException.java b/core/java/android/app/ServiceStartNotAllowedException.java new file mode 100644 index 000000000000..33285b2190eb --- /dev/null +++ b/core/java/android/app/ServiceStartNotAllowedException.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.annotation.NonNull; + +/** + * Exception thrown when an app tries to start a {@link Service} when it's not allowed to do so. + */ +public abstract class ServiceStartNotAllowedException extends IllegalStateException { + ServiceStartNotAllowedException(@NonNull String message) { + super(message); + } + + /** + * Return either {@link ForegroundServiceStartNotAllowedException} or + * {@link BackgroundServiceStartNotAllowedException} + * @hide + */ + @NonNull + public static ServiceStartNotAllowedException newInstance(boolean foreground, + @NonNull String message) { + if (foreground) { + return new ForegroundServiceStartNotAllowedException(message); + } else { + return new BackgroundServiceStartNotAllowedException(message); + } + } +} diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index ffaaa578cdd2..e16e40b6d572 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -47,6 +47,7 @@ import android.app.usage.IUsageStatsManager; import android.app.usage.NetworkStatsManager; import android.app.usage.StorageStatsManager; import android.app.usage.UsageStatsManager; +import android.apphibernation.AppHibernationManager; import android.appwidget.AppWidgetManager; import android.bluetooth.BluetoothManager; import android.companion.CompanionDeviceManager; @@ -99,6 +100,7 @@ import android.hardware.input.InputManager; import android.hardware.iris.IIrisService; import android.hardware.iris.IrisManager; import android.hardware.lights.LightsManager; +import android.hardware.lights.SystemLightsManager; import android.hardware.location.ContextHubManager; import android.hardware.radio.RadioManager; import android.hardware.usb.IUsbManager; @@ -1344,7 +1346,7 @@ public final class SystemServiceRegistry { @Override public LightsManager createService(ContextImpl ctx) throws ServiceNotFoundException { - return new LightsManager(ctx); + return new SystemLightsManager(ctx); }}); registerService(Context.INCREMENTAL_SERVICE, IncrementalManager.class, new CachedServiceFetcher<IncrementalManager>() { @@ -1377,6 +1379,13 @@ public final class SystemServiceRegistry { IBinder b = ServiceManager.getServiceOrThrow(Context.APP_INTEGRITY_SERVICE); return new AppIntegrityManager(IAppIntegrityManager.Stub.asInterface(b)); }}); + registerService(Context.APP_HIBERNATION_SERVICE, AppHibernationManager.class, + new CachedServiceFetcher<AppHibernationManager>() { + @Override + public AppHibernationManager createService(ContextImpl ctx) { + IBinder b = ServiceManager.getService(Context.APP_HIBERNATION_SERVICE); + return b == null ? null : new AppHibernationManager(ctx); + }}); registerService(Context.DREAM_SERVICE, DreamManager.class, new CachedServiceFetcher<DreamManager>() { @Override diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index 1e382307a1a3..f523a7d29713 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -43,7 +43,7 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { @Override @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public void onActivityPinned(String packageName, int userId, int taskId, int stackId) + public void onActivityPinned(String packageName, int userId, int taskId, int rootTaskId) throws RemoteException { } @@ -66,7 +66,7 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { @Override @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public void onActivityDismissingDockedStack() throws RemoteException { + public void onActivityDismissingDockedTask() throws RemoteException { } @Override diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java index 2d203f57a66f..3abba43ae0a8 100644 --- a/core/java/android/app/WallpaperColors.java +++ b/core/java/android/app/WallpaperColors.java @@ -30,14 +30,17 @@ import android.util.Log; import android.util.Size; import com.android.internal.graphics.ColorUtils; +import com.android.internal.graphics.palette.CelebiQuantizer; import com.android.internal.graphics.palette.Palette; -import com.android.internal.graphics.palette.VariationalKMeansQuantizer; import com.android.internal.util.ContrastColorUtil; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * Provides information about the colors of a wallpaper. @@ -94,16 +97,21 @@ public final class WallpaperColors implements Parcelable { private static final float DARK_PIXEL_CONTRAST = 6f; private static final float MAX_DARK_AREA = 0.025f; - private final ArrayList<Color> mMainColors; + private final List<Color> mMainColors; + private final Map<Integer, Integer> mAllColors; private int mColorHints; public WallpaperColors(Parcel parcel) { mMainColors = new ArrayList<>(); + mAllColors = new HashMap<>(); final int count = parcel.readInt(); for (int i = 0; i < count; i++) { final int colorInt = parcel.readInt(); Color color = Color.valueOf(colorInt); mMainColors.add(color); + + final int population = parcel.readInt(); + mAllColors.put(colorInt, population); } mColorHints = parcel.readInt(); } @@ -166,39 +174,22 @@ public final class WallpaperColors implements Parcelable { } final Palette palette = Palette - .from(bitmap) - .setQuantizer(new VariationalKMeansQuantizer()) - .maximumColorCount(5) - .clearFilters() + .from(bitmap, new CelebiQuantizer()) + .maximumColorCount(256) .resizeBitmapArea(MAX_WALLPAPER_EXTRACTION_AREA) .generate(); - // Remove insignificant colors and sort swatches by population final ArrayList<Palette.Swatch> swatches = new ArrayList<>(palette.getSwatches()); - final float minColorArea = bitmap.getWidth() * bitmap.getHeight() * MIN_COLOR_OCCURRENCE; - swatches.removeIf(s -> s.getPopulation() < minColorArea); swatches.sort((a, b) -> b.getPopulation() - a.getPopulation()); final int swatchesSize = swatches.size(); - Color primary = null, secondary = null, tertiary = null; - swatchLoop: + final Map<Integer, Integer> populationByColor = new HashMap<>(); for (int i = 0; i < swatchesSize; i++) { - Color color = Color.valueOf(swatches.get(i).getRgb()); - switch (i) { - case 0: - primary = color; - break; - case 1: - secondary = color; - break; - case 2: - tertiary = color; - break; - default: - // out of bounds - break swatchLoop; - } + Palette.Swatch swatch = swatches.get(i); + int colorInt = swatch.getInt(); + populationByColor.put(colorInt, swatch.getPopulation()); + } int hints = calculateDarkHints(bitmap); @@ -207,7 +198,7 @@ public final class WallpaperColors implements Parcelable { bitmap.recycle(); } - return new WallpaperColors(primary, secondary, tertiary, HINT_FROM_BITMAP | hints); + return new WallpaperColors(populationByColor, HINT_FROM_BITMAP | hints); } /** @@ -253,9 +244,13 @@ public final class WallpaperColors implements Parcelable { } mMainColors = new ArrayList<>(3); + mAllColors = new HashMap<>(); + mMainColors.add(primaryColor); + mAllColors.put(primaryColor.toArgb(), 0); if (secondaryColor != null) { mMainColors.add(secondaryColor); + mAllColors.put(secondaryColor.toArgb(), 0); } if (tertiaryColor != null) { if (secondaryColor == null) { @@ -263,8 +258,32 @@ public final class WallpaperColors implements Parcelable { + "secondaryColor is null"); } mMainColors.add(tertiaryColor); + mAllColors.put(tertiaryColor.toArgb(), 0); } + mColorHints = colorHints; + } + /** + * Constructs a new object from a set of colors, where hints can be specified. + * + * @param populationByColor Map with keys of colors, and value representing the number of + * occurrences of color in the wallpaper. + * @param colorHints A combination of WallpaperColor hints. + * @hide + * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT + * @see WallpaperColors#fromBitmap(Bitmap) + * @see WallpaperColors#fromDrawable(Drawable) + */ + public WallpaperColors(@NonNull Map<Integer, Integer> populationByColor, int colorHints) { + mAllColors = populationByColor; + + ArrayList<Map.Entry<Integer, Integer>> mapEntries = new ArrayList( + populationByColor.entrySet()); + mapEntries.sort((a, b) -> + a.getValue().compareTo(b.getValue()) + ); + mMainColors = mapEntries.stream().map(entry -> Color.valueOf(entry.getKey())).collect( + Collectors.toList()); mColorHints = colorHints; } @@ -293,6 +312,9 @@ public final class WallpaperColors implements Parcelable { for (int i = 0; i < count; i++) { Color color = mainColors.get(i); dest.writeInt(color.toArgb()); + Integer population = mAllColors.get(color.toArgb()); + int populationInt = (population != null) ? population : 0; + dest.writeInt(populationInt); } dest.writeInt(mColorHints); } @@ -336,6 +358,17 @@ public final class WallpaperColors implements Parcelable { return Collections.unmodifiableList(mMainColors); } + /** + * Map of all colors. Key is rgb integer, value is importance of color. + * + * @return List of colors. + * @hide + */ + public @NonNull Map<Integer, Integer> getAllColors() { + return Collections.unmodifiableMap(mAllColors); + } + + @Override public boolean equals(@Nullable Object o) { if (o == null || getClass() != o.getClass()) { diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 59e5144113c9..bb1ff6051d56 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2746,6 +2746,32 @@ public class DevicePolicyManager { @Retention(RetentionPolicy.SOURCE) public @interface PersonalAppsSuspensionReason {} + /** + * The default device owner type for a managed device. + * + * @hide + */ + public static final int DEVICE_OWNER_TYPE_DEFAULT = 0; + + /** + * The device owner type for a financed device. + * + * @hide + */ + public static final int DEVICE_OWNER_TYPE_FINANCED = 1; + + /** + * Different device owner types for a managed device. + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "DEVICE_OWNER_TYPE_" }, value = { + DEVICE_OWNER_TYPE_DEFAULT, + DEVICE_OWNER_TYPE_FINANCED + }) + public @interface DeviceOwnerType {} + /** @hide */ @TestApi public static final int OPERATION_LOCK_NOW = 1; @@ -7276,7 +7302,12 @@ public class DevicePolicyManager { /** * @hide */ + @TestApi @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresPermission(allOf = { + android.Manifest.permission.MANAGE_DEVICE_ADMINS, + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL + }) public void setActiveAdmin(@NonNull ComponentName policyReceiver, boolean refreshing, int userHandle) { if (mService != null) { @@ -7453,8 +7484,10 @@ public class DevicePolicyManager { * @throws IllegalArgumentException if the package name is null or invalid * @throws IllegalStateException If the preconditions mentioned are not met. */ - public boolean setDeviceOwner(ComponentName who, String ownerName, int userId) - throws IllegalArgumentException, IllegalStateException { + @TestApi + @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) + public boolean setDeviceOwner( + @NonNull ComponentName who, @Nullable String ownerName, @UserIdInt int userId) { if (mService != null) { try { return mService.setDeviceOwner(who, ownerName, userId); @@ -7521,7 +7554,10 @@ public class DevicePolicyManager { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, + }) public ComponentName getDeviceOwnerComponentOnAnyUser() { return getDeviceOwnerComponentInner(/* callingUserOnly =*/ false); } @@ -10477,9 +10513,10 @@ public class DevicePolicyManager { /** * Reset record of previous system update freeze period the device went through. - * Only callable by ADB. * @hide */ + @TestApi + @RequiresPermission(android.Manifest.permission.CLEAR_FREEZE_PERIOD) public void clearSystemUpdatePolicyFreezePeriodRecord() { throwIfParentInstance("clearSystemUpdatePolicyFreezePeriodRecord"); if (mService == null) { @@ -11207,9 +11244,11 @@ public class DevicePolicyManager { /** * Makes all accumulated network logs available to DPC in a new batch. - * Only callable by ADB. If throttled, returns time to wait in milliseconds, otherwise 0. + * If throttled, returns time to wait in milliseconds, otherwise 0. * @hide */ + @TestApi + @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs() { if (mService == null) { return -1; @@ -11223,9 +11262,11 @@ public class DevicePolicyManager { /** * Forces a batch of security logs to be fetched from logd and makes it available for DPC. - * Only callable by ADB. If throttled, returns time to wait in milliseconds, otherwise 0. + * If throttled, returns time to wait in milliseconds, otherwise 0. * @hide */ + @TestApi + @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs() { if (mService == null) { return 0; @@ -11657,7 +11698,10 @@ public class DevicePolicyManager { * @throws SecurityException if the caller is not shell / root or the admin package * isn't a test application see {@link ApplicationInfo#FLAG_TEST_APP}. */ - public void forceRemoveActiveAdmin(ComponentName adminReceiver, int userHandle) { + @TestApi + @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) + public void forceRemoveActiveAdmin( + @NonNull ComponentName adminReceiver, @UserIdInt int userHandle) { try { mService.forceRemoveActiveAdmin(adminReceiver, userHandle); } catch (RemoteException re) { @@ -12727,8 +12771,11 @@ public class DevicePolicyManager { * * @hide */ - @RequiresPermission(value = android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, - conditional = true) + @TestApi + @RequiresPermission(anyOf = { + android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, + android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS + }, conditional = true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull ComponentName who) { if (mService == null) { return; @@ -13460,6 +13507,57 @@ public class DevicePolicyManager { } /** + * Sets the device owner type for a managed device (e.g. financed device). + * + * @param admin The {@link DeviceAdminReceiver} that is the device owner. + * @param deviceOwnerType The device owner type is set to. Use + * {@link #DEVICE_OWNER_TYPE_DEFAULT} for the default device owner type. Use + * {@link #DEVICE_OWNER_TYPE_FINANCED} for the financed device owner type. + * + * @throws IllegalStateException When admin is not the device owner, or there is no device + * owner, or attempting to set the device owner type again for the same admin. + * @throws SecurityException If the caller does not have the permission + * {@link permission#MANAGE_PROFILE_AND_DEVICE_OWNERS}. + * + * @hide + */ + public void setDeviceOwnerType(@NonNull ComponentName admin, + @DeviceOwnerType int deviceOwnerType) { + throwIfParentInstance("setDeviceOwnerType"); + if (mService != null) { + try { + mService.setDeviceOwnerType(admin, deviceOwnerType); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + } + + /** + * Returns the device owner type for the admin used in + * {@link #setDeviceOwnerType(ComponentName, int)}. {@link #DEVICE_OWNER_TYPE_DEFAULT} + * would be returned when the device owner type is not set for the device owner admin. + * + * @param admin The {@link DeviceAdminReceiver} that is the device owner. + * + * @throws IllegalStateException When admin is not the device owner or there is no device owner. + * + * @hide + */ + @DeviceOwnerType + public int getDeviceOwnerType(@NonNull ComponentName admin) { + throwIfParentInstance("getDeviceOwnerType"); + if (mService != null) { + try { + return mService.getDeviceOwnerType(admin); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + return DEVICE_OWNER_TYPE_DEFAULT; + } + + /** * Called by device owner or profile owner of an organization-owned managed profile to * enable or disable USB data signaling for the device. When disabled, USB data connections * (except from charging functions) are prohibited. diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 8a87b16b760b..ac1592d2d2a1 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -503,6 +503,9 @@ interface IDevicePolicyManager { UserHandle createAndProvisionManagedProfile(in ManagedProfileProvisioningParams provisioningParams, in String callerPackage); void provisionFullyManagedDevice(in FullyManagedDeviceProvisioningParams provisioningParams, in String callerPackage); + void setDeviceOwnerType(in ComponentName admin, in int deviceOwnerType); + int getDeviceOwnerType(in ComponentName admin); + void resetDefaultCrossProfileIntentFilters(int userId); boolean canAdminGrantSensorsPermissionsForUser(int userId); diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java index 673de8fa7c8c..dae565e12fd7 100644 --- a/core/java/android/app/backup/BackupManager.java +++ b/core/java/android/app/backup/BackupManager.java @@ -361,36 +361,7 @@ public class BackupManager { try { // All packages, current transport IRestoreSession binder = - sService.beginRestoreSessionForUser(mContext.getUserId(), null, null, - OperationType.BACKUP); - if (binder != null) { - session = new RestoreSession(mContext, binder); - } - } catch (RemoteException e) { - Log.e(TAG, "beginRestoreSession() couldn't connect"); - } - } - return session; - } - - /** - * Begin the process of restoring data from backup. See the - * {@link android.app.backup.RestoreSession} class for documentation on that process. - * - * @param operationType Type of the operation, see {@link OperationType} - * - * @hide - */ - @RequiresPermission(android.Manifest.permission.BACKUP) - public RestoreSession beginRestoreSession(@OperationType int operationType) { - RestoreSession session = null; - checkServiceBinder(); - if (sService != null) { - try { - // All packages, current transport - IRestoreSession binder = - sService.beginRestoreSessionForUser(mContext.getUserId(), null, null, - operationType); + sService.beginRestoreSessionForUser(mContext.getUserId(), null, null); if (binder != null) { session = new RestoreSession(mContext, binder); } @@ -801,7 +772,7 @@ public class BackupManager { @SystemApi @RequiresPermission(android.Manifest.permission.BACKUP) public int requestBackup(String[] packages, BackupObserver observer) { - return requestBackup(packages, observer, null, 0, OperationType.BACKUP); + return requestBackup(packages, observer, null, 0); } /** @@ -826,31 +797,6 @@ public class BackupManager { @RequiresPermission(android.Manifest.permission.BACKUP) public int requestBackup(String[] packages, BackupObserver observer, BackupManagerMonitor monitor, int flags) { - return requestBackup(packages, observer, monitor, flags, OperationType.BACKUP); - } - - /** - * Request an immediate backup, providing an observer to which results of the backup operation - * will be published. The Android backup system will decide for each package whether it will - * be full app data backup or key/value-pair-based backup. - * - * <p>If this method returns {@link BackupManager#SUCCESS}, the OS will attempt to backup all - * provided packages using the remote transport. - * - * @param packages List of package names to backup. - * @param observer The {@link BackupObserver} to receive callbacks during the backup - * operation. Could be {@code null}. - * @param monitor The {@link BackupManagerMonitorWrapper} to receive callbacks of important - * events during the backup operation. Could be {@code null}. - * @param flags {@link #FLAG_NON_INCREMENTAL_BACKUP}. - * @param operationType {@link OperationType} - * @return {@link BackupManager#SUCCESS} on success; nonzero on error. - * @throws IllegalArgumentException on null or empty {@code packages} param. - * @hide - */ - @RequiresPermission(android.Manifest.permission.BACKUP) - public int requestBackup(String[] packages, BackupObserver observer, - BackupManagerMonitor monitor, int flags, @OperationType int operationType) { checkServiceBinder(); if (sService != null) { try { @@ -860,8 +806,7 @@ public class BackupManager { BackupManagerMonitorWrapper monitorWrapper = monitor == null ? null : new BackupManagerMonitorWrapper(monitor); - return sService.requestBackup(packages, observerWrapper, monitorWrapper, flags, - operationType); + return sService.requestBackup(packages, observerWrapper, monitorWrapper, flags); } catch (RemoteException e) { Log.e(TAG, "requestBackup() couldn't connect"); } diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java index f7ed6f1f2feb..3701ea825933 100644 --- a/core/java/android/app/backup/FullBackup.java +++ b/core/java/android/app/backup/FullBackup.java @@ -19,8 +19,10 @@ package android.app.backup; import static android.app.backup.BackupManager.OperationType; import android.annotation.Nullable; +import android.annotation.StringDef; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.XmlResourceParser; import android.os.ParcelFileDescriptor; @@ -33,6 +35,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; +import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -93,6 +96,15 @@ public class FullBackup { public static final String FLAG_REQUIRED_FAKE_CLIENT_SIDE_ENCRYPTION = "fakeClientSideEncryption"; + @StringDef({ + ConfigSection.CLOUD_BACKUP, + ConfigSection.DEVICE_TRANSFER + }) + private @interface ConfigSection { + String CLOUD_BACKUP = "cloud-backup"; + String DEVICE_TRANSFER = "device-transfer"; + } + /** * Identify {@link BackupScheme} object by package and operation type * (see {@link OperationType}) it corresponds to. @@ -273,6 +285,7 @@ public class FullBackup { private final static String TAG_INCLUDE = "include"; private final static String TAG_EXCLUDE = "exclude"; + final int mDataExtractionRules; final int mFullBackupContent; @OperationType final int mOperationType; final PackageManager mPackageManager; @@ -394,7 +407,10 @@ public class FullBackup { ArraySet<PathWithRequiredFlags> mExcludes; BackupScheme(Context context, @OperationType int operationType) { - mFullBackupContent = context.getApplicationInfo().fullBackupContent; + ApplicationInfo applicationInfo = context.getApplicationInfo(); + + mDataExtractionRules = applicationInfo.dataExtractionRulesRes; + mFullBackupContent = applicationInfo.fullBackupContent; mOperationType = operationType; mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); mPackageManager = context.getPackageManager(); @@ -468,34 +484,98 @@ public class FullBackup { mIncludes = new ArrayMap<String, Set<PathWithRequiredFlags>>(); mExcludes = new ArraySet<PathWithRequiredFlags>(); - if (mFullBackupContent == 0) { - // android:fullBackupContent="true" which means that we'll do everything. + if (mFullBackupContent == 0 && mDataExtractionRules == 0) { + // No scheme specified via either new or legacy config, will copy everything. if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) { Log.v(FullBackup.TAG_XML_PARSER, "android:fullBackupContent - \"true\""); } } else { - // android:fullBackupContent="@xml/some_resource". + // Scheme is present. if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) { - Log.v(FullBackup.TAG_XML_PARSER, - "android:fullBackupContent - found xml resource"); + Log.v(FullBackup.TAG_XML_PARSER, "Found xml scheme: " + + "android:fullBackupContent=" + mFullBackupContent + + "; android:dataExtractionRules=" + mDataExtractionRules); } - XmlResourceParser parser = null; + try { - parser = mPackageManager - .getResourcesForApplication(mPackageName) - .getXml(mFullBackupContent); - parseBackupSchemeFromXmlLocked(parser, mExcludes, mIncludes); + parseSchemeForOperationType(mOperationType); } catch (PackageManager.NameNotFoundException e) { // Throw it as an IOException throw new IOException(e); - } finally { - if (parser != null) { - parser.close(); - } } } } + private void parseSchemeForOperationType(@OperationType int operationType) + throws PackageManager.NameNotFoundException, IOException, XmlPullParserException { + String configSection = getConfigSectionForOperationType(operationType); + if (configSection == null) { + Slog.w(TAG, "Given operation type isn't supported by backup scheme: " + + operationType); + return; + } + + if (mDataExtractionRules != 0) { + // New config is present. Use it if it has configuration for this operation + // type. + try (XmlResourceParser parser = getParserForResource(mDataExtractionRules)) { + parseNewBackupSchemeFromXmlLocked(parser, configSection, mExcludes, mIncludes); + } + if (!mExcludes.isEmpty() || !mIncludes.isEmpty()) { + // Found configuration in the new config, we will use it. + return; + } + } + + // TODO(b/180523564): Ignore the old config for apps targeting Android S+ during D2D. + + if (mFullBackupContent != 0) { + // Fall back to the old config. + try (XmlResourceParser parser = getParserForResource(mFullBackupContent)) { + parseBackupSchemeFromXmlLocked(parser, mExcludes, mIncludes); + } + } + } + + @Nullable + private String getConfigSectionForOperationType(@OperationType int operationType) { + switch (operationType) { + case OperationType.BACKUP: + return ConfigSection.CLOUD_BACKUP; + case OperationType.MIGRATION: + return ConfigSection.DEVICE_TRANSFER; + default: + return null; + } + } + + private XmlResourceParser getParserForResource(int resourceId) + throws PackageManager.NameNotFoundException { + return mPackageManager + .getResourcesForApplication(mPackageName) + .getXml(resourceId); + } + + private void parseNewBackupSchemeFromXmlLocked(XmlPullParser parser, + @ConfigSection String configSection, + Set<PathWithRequiredFlags> excludes, + Map<String, Set<PathWithRequiredFlags>> includes) + throws IOException, XmlPullParserException { + verifyTopLevelTag(parser, "data-extraction-rules"); + + int event; + while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (event != XmlPullParser.START_TAG || !configSection.equals(parser.getName())) { + continue; + } + + // TODO(b/180523028): Parse required attributes for rules (e.g. encryption). + parseRules(parser, excludes, includes, Optional.of(0), configSection); + } + + logParsingResults(excludes, includes); + } + @VisibleForTesting public void parseBackupSchemeFromXmlLocked(XmlPullParser parser, Set<PathWithRequiredFlags> excludes, @@ -503,7 +583,7 @@ public class FullBackup { throws IOException, XmlPullParserException { verifyTopLevelTag(parser, "full-backup-content"); - parseRules(parser, excludes, includes, Optional.empty()); + parseRules(parser, excludes, includes, Optional.empty(), "full-backup-content"); logParsingResults(excludes, includes); } @@ -532,10 +612,12 @@ public class FullBackup { private void parseRules(XmlPullParser parser, Set<PathWithRequiredFlags> excludes, Map<String, Set<PathWithRequiredFlags>> includes, - Optional<Integer> maybeRequiredFlags) + Optional<Integer> maybeRequiredFlags, + String endingTag) throws IOException, XmlPullParserException { int event; - while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { + while ((event = parser.next()) != XmlPullParser.END_DOCUMENT + && !parser.getName().equals(endingTag)) { switch (event) { case XmlPullParser.START_TAG: validateInnerTagContents(parser); diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl index e1bbc08e72f3..bf5be95c4ab0 100644 --- a/core/java/android/app/backup/IBackupManager.aidl +++ b/core/java/android/app/backup/IBackupManager.aidl @@ -547,11 +547,9 @@ interface IBackupManager { * set can be restored. * @param transportID The name of the transport to use for the restore operation. * May be null, in which case the current active transport is used. - * @param operationType Type of the operation, see {@link BackupManager#OperationType} * @return An interface to the restore session, or null on error. */ - IRestoreSession beginRestoreSessionForUser(int userId, String packageName, String transportID, - int operationType); + IRestoreSession beginRestoreSessionForUser(int userId, String packageName, String transportID); /** * Notify the backup manager that a BackupAgent has completed the operation @@ -680,7 +678,7 @@ interface IBackupManager { * {@link android.app.backup.IBackupManager.requestBackupForUser} for the calling user id. */ int requestBackup(in String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor, - int flags, int operationType); + int flags); /** * Cancel all running backups. After this call returns, no currently running backups will diff --git a/core/java/android/app/people/PeopleSpaceTile.java b/core/java/android/app/people/PeopleSpaceTile.java index 9a53b99aaefe..132af4b2f67b 100644 --- a/core/java/android/app/people/PeopleSpaceTile.java +++ b/core/java/android/app/people/PeopleSpaceTile.java @@ -53,6 +53,7 @@ public class PeopleSpaceTile implements Parcelable { private boolean mIsImportantConversation; private String mNotificationKey; private CharSequence mNotificationContent; + private String mNotificationCategory; private Uri mNotificationDataUri; private Intent mIntent; private long mNotificationTimestamp; @@ -70,6 +71,7 @@ public class PeopleSpaceTile implements Parcelable { mIsImportantConversation = b.mIsImportantConversation; mNotificationKey = b.mNotificationKey; mNotificationContent = b.mNotificationContent; + mNotificationCategory = b.mNotificationCategory; mNotificationDataUri = b.mNotificationDataUri; mIntent = b.mIntent; mNotificationTimestamp = b.mNotificationTimestamp; @@ -129,6 +131,10 @@ public class PeopleSpaceTile implements Parcelable { return mNotificationContent; } + public String getNotificationCategory() { + return mNotificationCategory; + } + public Uri getNotificationDataUri() { return mNotificationDataUri; } @@ -166,6 +172,7 @@ public class PeopleSpaceTile implements Parcelable { builder.setIsImportantConversation(mIsImportantConversation); builder.setNotificationKey(mNotificationKey); builder.setNotificationContent(mNotificationContent); + builder.setNotificationCategory(mNotificationCategory); builder.setNotificationDataUri(mNotificationDataUri); builder.setIntent(mIntent); builder.setNotificationTimestamp(mNotificationTimestamp); @@ -186,6 +193,7 @@ public class PeopleSpaceTile implements Parcelable { private boolean mIsImportantConversation; private String mNotificationKey; private CharSequence mNotificationContent; + private String mNotificationCategory; private Uri mNotificationDataUri; private Intent mIntent; private long mNotificationTimestamp; @@ -299,6 +307,12 @@ public class PeopleSpaceTile implements Parcelable { return this; } + /** Sets the associated notification's category. */ + public Builder setNotificationCategory(String notificationCategory) { + mNotificationCategory = notificationCategory; + return this; + } + /** Sets the associated notification's data URI. */ public Builder setNotificationDataUri(Uri notificationDataUri) { mNotificationDataUri = notificationDataUri; @@ -342,6 +356,7 @@ public class PeopleSpaceTile implements Parcelable { mIsImportantConversation = in.readBoolean(); mNotificationKey = in.readString(); mNotificationContent = in.readCharSequence(); + mNotificationCategory = in.readString(); mNotificationDataUri = in.readParcelable(Uri.class.getClassLoader()); mIntent = in.readParcelable(Intent.class.getClassLoader()); mNotificationTimestamp = in.readLong(); @@ -367,6 +382,7 @@ public class PeopleSpaceTile implements Parcelable { dest.writeBoolean(mIsImportantConversation); dest.writeString(mNotificationKey); dest.writeCharSequence(mNotificationContent); + dest.writeString(mNotificationCategory); dest.writeParcelable(mNotificationDataUri, flags); dest.writeParcelable(mIntent, flags); dest.writeLong(mNotificationTimestamp); diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 4277292e19a3..fc54c716d4ec 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -40,6 +40,7 @@ import android.util.AttributeSet; import android.util.Log; import android.util.Pair; import android.util.SparseArray; +import android.util.SparseIntArray; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; @@ -92,7 +93,10 @@ public class AppWidgetHostView extends FrameLayout { int mLayoutId = -1; private InteractionHandler mInteractionHandler; private boolean mOnLightBackground; - PointF mCurrentSize = null; + private PointF mCurrentSize = null; + private RemoteViews.ColorResources mColorResources = null; + // Stores the last remote views last inflated. + private RemoteViews mLastInflatedRemoteViews = null; private Executor mAsyncExecutor; private CancellationSignal mLastExecutionSignal; @@ -358,7 +362,7 @@ public class AppWidgetHostView extends FrameLayout { PointF newSize = new PointF(size.x - xPaddingDips, size.y - yPaddingDips); if (!newSize.equals(mCurrentSize)) { mCurrentSize = newSize; - mLayoutId = -1; // Prevents recycling the view. + reapplyLastRemoteViews(); } } @@ -368,7 +372,7 @@ public class AppWidgetHostView extends FrameLayout { public void clearCurrentSize() { if (mCurrentSize != null) { mCurrentSize = null; - mLayoutId = -1; + reapplyLastRemoteViews(); } } @@ -477,10 +481,18 @@ public class AppWidgetHostView extends FrameLayout { * AppWidget provider. Will animate into these new views as needed */ public void updateAppWidget(RemoteViews remoteViews) { + this.mLastInflatedRemoteViews = remoteViews; applyRemoteViews(remoteViews, true); } /** + * Reapply the last inflated remote views, or the default view is none was inflated. + */ + private void reapplyLastRemoteViews() { + applyRemoteViews(mLastInflatedRemoteViews, true); + } + + /** * @hide */ protected void applyRemoteViews(RemoteViews remoteViews, boolean useAsyncIfPossible) { @@ -518,7 +530,8 @@ public class AppWidgetHostView extends FrameLayout { // layout matches, try recycling it if (content == null && layoutId == mLayoutId) { try { - remoteViews.reapply(mContext, mView, mInteractionHandler); + remoteViews.reapply(mContext, mView, mInteractionHandler, mCurrentSize, + mColorResources); content = mView; recycled = true; if (LOGD) Log.d(TAG, "was able to recycle existing layout"); @@ -530,7 +543,8 @@ public class AppWidgetHostView extends FrameLayout { // Try normal RemoteView inflation if (content == null) { try { - content = remoteViews.apply(mContext, this, mInteractionHandler, mCurrentSize); + content = remoteViews.apply(mContext, this, mInteractionHandler, + mCurrentSize, mColorResources); if (LOGD) Log.d(TAG, "had to inflate new layout"); } catch (RuntimeException e) { exception = e; @@ -583,7 +597,8 @@ public class AppWidgetHostView extends FrameLayout { mAsyncExecutor, new ViewApplyListener(remoteViews, layoutId, true), mInteractionHandler, - mCurrentSize); + mCurrentSize, + mColorResources); } catch (Exception e) { // Reapply failed. Try apply } @@ -594,7 +609,8 @@ public class AppWidgetHostView extends FrameLayout { mAsyncExecutor, new ViewApplyListener(remoteViews, layoutId, false), mInteractionHandler, - mCurrentSize); + mCurrentSize, + mColorResources); } } @@ -662,9 +678,13 @@ public class AppWidgetHostView extends FrameLayout { protected Context getRemoteContext() { try { // Return if cloned successfully, otherwise default - return mContext.createApplicationContext( + Context newContext = mContext.createApplicationContext( mInfo.providerInfo.applicationInfo, Context.CONTEXT_RESTRICTED); + if (mColorResources != null) { + mColorResources.apply(newContext); + } + return newContext; } catch (NameNotFoundException e) { Log.e(TAG, "Package name " + mInfo.providerInfo.packageName + " not found"); return mContext; @@ -819,4 +839,37 @@ public class AppWidgetHostView extends FrameLayout { } }; } + + /** + * Set the dynamically overloaded color resources. + * + * {@code colorMapping} maps a predefined set of color resources to their ARGB + * representation. Any entry not in the predefined set of colors will be ignored. + * + * Calling this method will trigger a full re-inflation of the App Widget. + * + * The color resources that can be overloaded are the ones whose name is prefixed with + * {@code system_primary_}, {@code system_secondary_} or {@code system_neutral_}, for example + * {@link android.R.color#system_primary_500}. + */ + public void setColorResources(@NonNull SparseIntArray colorMapping) { + mColorResources = RemoteViews.ColorResources.create(mContext, colorMapping); + mLayoutId = -1; + reapplyLastRemoteViews(); + } + + /** + * Reset the dynamically overloaded resources, reverting to the default values for + * all the colors. + * + * If colors were defined before, calling this method will trigger a full re-inflation of the + * App Widget. + */ + public void resetColorResources() { + if (mColorResources != null) { + mColorResources = null; + mLayoutId = -1; + reapplyLastRemoteViews(); + } + } } diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index aac8710e8691..38919f61d9df 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -23,6 +23,8 @@ import android.annotation.RequiresFeature; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemService; +import android.annotation.TestApi; +import android.annotation.UserIdInt; import android.app.IServiceConnection; import android.app.PendingIntent; import android.compat.annotation.UnsupportedAppUsage; @@ -1095,7 +1097,9 @@ public class AppWidgetManager { * * @hide */ - public void setBindAppWidgetPermission(String packageName, int userId, boolean permission) { + @TestApi + public void setBindAppWidgetPermission( + @NonNull String packageName, @UserIdInt int userId, boolean permission) { if (mService == null) { return; } diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java index d893a5e49aa9..6ac1c1ae61ec 100644 --- a/core/java/android/appwidget/AppWidgetProviderInfo.java +++ b/core/java/android/appwidget/AppWidgetProviderInfo.java @@ -332,12 +332,13 @@ public class AppWidgetProviderInfo implements Parcelable { /** * Resource id for the description of the AppWidget. + * * <p>This field corresponds to the <code>android:description</code> attribute in the AppWidget * meta-data file. */ @SuppressLint("MutableBareField") @IdRes - public int descriptionResource; + public int descriptionRes; /** * Flags indicating various features supported by the widget. These are hints to the widget @@ -385,7 +386,7 @@ public class AppWidgetProviderInfo implements Parcelable { this.widgetCategory = in.readInt(); this.providerInfo = in.readTypedObject(ActivityInfo.CREATOR); this.widgetFeatures = in.readInt(); - this.descriptionResource = in.readInt(); + this.descriptionRes = in.readInt(); } /** @@ -442,14 +443,22 @@ public class AppWidgetProviderInfo implements Parcelable { return loadDrawable(context, density, previewImage, false); } - /** Loads localized description for the app widget. */ + /** + * Loads localized description for the app widget. + * + * <p>Description is intended to be displayed in the UI of the widget picker. + * + * @param context Context for accessing resources. + * + * @return CharSequence for app widget description for the current locale. + */ @Nullable - public final String loadDescription(@NonNull Context context) { - if (ResourceId.isValid(descriptionResource)) { + public final CharSequence loadDescription(@NonNull Context context) { + if (ResourceId.isValid(descriptionRes)) { return context.getPackageManager() .getText( providerInfo.packageName, - descriptionResource, + descriptionRes, providerInfo.applicationInfo) .toString() .trim(); @@ -499,7 +508,7 @@ public class AppWidgetProviderInfo implements Parcelable { out.writeInt(this.widgetCategory); out.writeTypedObject(this.providerInfo, flags); out.writeInt(this.widgetFeatures); - out.writeInt(this.descriptionResource); + out.writeInt(this.descriptionRes); } @Override @@ -528,7 +537,7 @@ public class AppWidgetProviderInfo implements Parcelable { that.widgetCategory = this.widgetCategory; that.providerInfo = this.providerInfo; that.widgetFeatures = this.widgetFeatures; - that.descriptionResource = this.descriptionResource; + that.descriptionRes = this.descriptionRes; return that; } diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index cd91aa9b16b7..53aaae0470e2 100644 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java @@ -943,12 +943,13 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean setBufferMillis(@BluetoothCodecConfig.SourceCodecType int codec, int value) { - if (VDBG) log("setBufferMillis(" + codec + ", " + value + ")"); + public boolean setBufferLengthMillis(@BluetoothCodecConfig.SourceCodecType int codec, + int value) { + if (VDBG) log("setBufferLengthMillis(" + codec + ", " + value + ")"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - return service.setBufferMillis(codec, value); + return service.setBufferLengthMillis(codec, value); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index ec46da0dcf0e..8d4157259ff7 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -3654,12 +3654,12 @@ public final class BluetoothAdapter { } @Override - public void onDeviceDisconnected(BluetoothDevice device) { + public void onDeviceDisconnected(BluetoothDevice device, int hciReason) { for (Map.Entry<BluetoothConnectionCallback, Executor> callbackExecutorEntry: mBluetoothConnectionCallbackExecutorMap.entrySet()) { BluetoothConnectionCallback callback = callbackExecutorEntry.getKey(); Executor executor = callbackExecutorEntry.getValue(); - executor.execute(() -> callback.onDeviceDisconnected(device)); + executor.execute(() -> callback.onDeviceDisconnected(device, hciReason)); } } }; @@ -3764,8 +3764,155 @@ public final class BluetoothAdapter { /** * Callback triggered when a bluetooth device (classic or BLE) is disconnected * @param device is the disconnected bluetooth device + * @param reason is the disconnect reason */ - public void onDeviceDisconnected(BluetoothDevice device) {} + public void onDeviceDisconnected(BluetoothDevice device, @DisconnectReason int reason) {} + + /** + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "REASON_" }, value = { + REASON_UNKNOWN, + REASON_LOCAL_REQUEST, + REASON_REMOTE_REQUEST, + REASON_LOCAL_ERROR, + REASON_REMOTE_ERROR, + REASON_TIMEOUT, + REASON_SECURITY, + REASON_SYSTEM_POLICY, + REASON_RESOURCE_LIMIT_REACHED, + REASON_CONNECTION_EXISTS, + REASON_BAD_PARAMETERS}) + public @interface DisconnectReason {} + + /** + * Indicates that the ACL disconnected due to an unknown reason. + */ + public static final int REASON_UNKNOWN = 0; + + /** + * Indicates that the ACL disconnected due to an explicit request from the local device. + * <p> + * Example cause: This is a normal disconnect reason, e.g., user/app initiates + * disconnection. + */ + public static final int REASON_LOCAL_REQUEST = 1; + + /** + * Indicates that the ACL disconnected due to an explicit request from the remote device. + * <p> + * Example cause: This is a normal disconnect reason, e.g., user/app initiates + * disconnection. + * <p> + * Example solution: The app can also prompt the user to check their remote device. + */ + public static final int REASON_REMOTE_REQUEST = 2; + + /** + * Generic disconnect reason indicating the ACL disconnected due to an error on the local + * device. + * <p> + * Example solution: Prompt the user to check their local device (e.g., phone, car + * headunit). + */ + public static final int REASON_LOCAL_ERROR = 3; + + /** + * Generic disconnect reason indicating the ACL disconnected due to an error on the remote + * device. + * <p> + * Example solution: Prompt the user to check their remote device (e.g., headset, car + * headunit, watch). + */ + public static final int REASON_REMOTE_ERROR = 4; + + /** + * Indicates that the ACL disconnected due to a timeout. + * <p> + * Example cause: remote device might be out of range. + * <p> + * Example solution: Prompt user to verify their remote device is on or in + * connection/pairing mode. + */ + public static final int REASON_TIMEOUT = 5; + + /** + * Indicates that the ACL disconnected due to link key issues. + * <p> + * Example cause: Devices are either unpaired or remote device is refusing our pairing + * request. + * <p> + * Example solution: Prompt user to unpair and pair again. + */ + public static final int REASON_SECURITY = 6; + + /** + * Indicates that the ACL disconnected due to the local device's system policy. + * <p> + * Example cause: privacy policy, power management policy, permissions, etc. + * <p> + * Example solution: Prompt the user to check settings, or check with their system + * administrator (e.g. some corp-managed devices do not allow OPP connection). + */ + public static final int REASON_SYSTEM_POLICY = 7; + + /** + * Indicates that the ACL disconnected due to resource constraints, either on the local + * device or the remote device. + * <p> + * Example cause: controller is busy, memory limit reached, maximum number of connections + * reached. + * <p> + * Example solution: The app should wait and try again. If still failing, prompt the user + * to disconnect some devices, or toggle Bluetooth on the local and/or the remote device. + */ + public static final int REASON_RESOURCE_LIMIT_REACHED = 8; + + /** + * Indicates that the ACL disconnected because another ACL connection already exists. + */ + public static final int REASON_CONNECTION_EXISTS = 9; + + /** + * Indicates that the ACL disconnected due to incorrect parameters passed in from the app. + * <p> + * Example solution: Change parameters and try again. If error persists, the app can report + * telemetry and/or log the error in a bugreport. + */ + public static final int REASON_BAD_PARAMETERS = 10; + + /** + * Returns human-readable strings corresponding to {@link DisconnectReason}. + */ + public static String disconnectReasonText(@DisconnectReason int reason) { + switch (reason) { + case REASON_UNKNOWN: + return "Reason unknown"; + case REASON_LOCAL_REQUEST: + return "Local request"; + case REASON_REMOTE_REQUEST: + return "Remote request"; + case REASON_LOCAL_ERROR: + return "Local error"; + case REASON_REMOTE_ERROR: + return "Remote error"; + case REASON_TIMEOUT: + return "Timeout"; + case REASON_SECURITY: + return "Security"; + case REASON_SYSTEM_POLICY: + return "System policy"; + case REASON_RESOURCE_LIMIT_REACHED: + return "Resource constrained"; + case REASON_CONNECTION_EXISTS: + return "Connection already exists"; + case REASON_BAD_PARAMETERS: + return "Bad parameters"; + default: + return "Unrecognized disconnect reason: " + reason; + } + } } /** diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java index ff6cffb272a5..0312a2190a4b 100644 --- a/core/java/android/bluetooth/BluetoothMapClient.java +++ b/core/java/android/bluetooth/BluetoothMapClient.java @@ -18,7 +18,9 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.app.PendingIntent; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -30,6 +32,7 @@ import android.os.RemoteException; import android.util.Log; import java.util.ArrayList; +import java.util.Collection; import java.util.List; /** @@ -37,44 +40,60 @@ import java.util.List; * * @hide */ +@SystemApi public final class BluetoothMapClient implements BluetoothProfile { private static final String TAG = "BluetoothMapClient"; private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE); + /** @hide */ public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED"; + /** @hide */ public static final String ACTION_MESSAGE_RECEIVED = "android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED"; /* Actions to be used for pending intents */ + /** @hide */ public static final String ACTION_MESSAGE_SENT_SUCCESSFULLY = "android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY"; + /** @hide */ public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY = "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY"; /** * Action to notify read status changed + * + * @hide */ public static final String ACTION_MESSAGE_READ_STATUS_CHANGED = "android.bluetooth.mapmce.profile.action.MESSAGE_READ_STATUS_CHANGED"; /** * Action to notify deleted status changed + * + * @hide */ public static final String ACTION_MESSAGE_DELETED_STATUS_CHANGED = "android.bluetooth.mapmce.profile.action.MESSAGE_DELETED_STATUS_CHANGED"; - /* Extras used in ACTION_MESSAGE_RECEIVED intent. - * NOTE: HANDLE is only valid for a single session with the device. */ + /** + * Extras used in ACTION_MESSAGE_RECEIVED intent. + * NOTE: HANDLE is only valid for a single session with the device. + */ + /** @hide */ public static final String EXTRA_MESSAGE_HANDLE = "android.bluetooth.mapmce.profile.extra.MESSAGE_HANDLE"; + /** @hide */ public static final String EXTRA_MESSAGE_TIMESTAMP = "android.bluetooth.mapmce.profile.extra.MESSAGE_TIMESTAMP"; + /** @hide */ public static final String EXTRA_MESSAGE_READ_STATUS = "android.bluetooth.mapmce.profile.extra.MESSAGE_READ_STATUS"; + /** @hide */ public static final String EXTRA_SENDER_CONTACT_URI = "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_URI"; + /** @hide */ public static final String EXTRA_SENDER_CONTACT_NAME = "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_NAME"; @@ -84,6 +103,8 @@ public final class BluetoothMapClient implements BluetoothProfile { * Possible values are: * true: deleted * false: undeleted + * + * @hide */ public static final String EXTRA_MESSAGE_DELETED_STATUS = "android.bluetooth.mapmce.profile.extra.MESSAGE_DELETED_STATUS"; @@ -93,24 +114,42 @@ public final class BluetoothMapClient implements BluetoothProfile { * Possible values are: * 0: failure * 1: success + * + * @hide */ public static final String EXTRA_RESULT_CODE = "android.bluetooth.device.extra.RESULT_CODE"; - /** There was an error trying to obtain the state */ + /** + * There was an error trying to obtain the state + * @hide + */ public static final int STATE_ERROR = -1; + /** @hide */ public static final int RESULT_FAILURE = 0; + /** @hide */ public static final int RESULT_SUCCESS = 1; - /** Connection canceled before completion. */ + /** + * Connection canceled before completion. + * @hide + */ public static final int RESULT_CANCELED = 2; - + /** @hide */ private static final int UPLOADING_FEATURE_BITMASK = 0x08; - /** Parameters in setMessageStatus */ + /* + * UNREAD, READ, UNDELETED, DELETED are passed as parameters + * to setMessageStatus to indicate the messages new state. + */ + + /** @hide */ public static final int UNREAD = 0; + /** @hide */ public static final int READ = 1; + /** @hide */ public static final int UNDELETED = 2; + /** @hide */ public static final int DELETED = 3; private BluetoothAdapter mAdapter; @@ -132,19 +171,12 @@ public final class BluetoothMapClient implements BluetoothProfile { mProfileConnector.connect(context, listener); } - protected void finalize() throws Throwable { - try { - close(); - } finally { - super.finalize(); - } - } - /** * Close the connection to the backing service. * Other public functions of BluetoothMap will return default error * results once close() has been called. Multiple invocations of close() * are ok. + * @hide */ public void close() { mProfileConnector.disconnect(); @@ -158,6 +190,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * Returns true if the specified Bluetooth device is connected. * Returns false if not connected, or if this proxy object is not * currently connected to the Map service. + * @hide */ public boolean isConnected(BluetoothDevice device) { if (VDBG) Log.d(TAG, "isConnected(" + device + ")"); @@ -225,6 +258,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * Get the list of connected devices. Currently at most one. * * @return list of connected devices + * @hide */ @Override public List<BluetoothDevice> getConnectedDevices() { @@ -246,6 +280,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * Get the list of devices matching specified states. Currently at most one. * * @return list of matching devices + * @hide */ @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { @@ -267,6 +302,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * Get connection state of device * * @return device connection state + * @hide */ @Override public int getConnectionState(BluetoothDevice device) { @@ -383,11 +419,44 @@ public final class BluetoothMapClient implements BluetoothProfile { * Send an SMS message to either the contacts primary number or the telephone number specified. * * @param device Bluetooth device + * @param contacts Uri Collection of the contacts + * @param message Message to be sent + * @param sentIntent intent issued when message is sent + * @param deliveredIntent intent issued when message is delivered + * @return true if the message is enqueued, false on error + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.SEND_SMS) + public boolean sendMessage(@NonNull BluetoothDevice device, @NonNull Collection<Uri> contacts, + @NonNull String message, @Nullable PendingIntent sentIntent, + @Nullable PendingIntent deliveredIntent) { + if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message); + final IBluetoothMapClient service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + try { + return service.sendMessage(device, contacts.toArray(new Uri[contacts.size()]), + message, sentIntent, deliveredIntent); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; + } + } + return false; + } + + /** + * Send a message. + * + * Send an SMS message to either the contacts primary number or the telephone number specified. + * + * @param device Bluetooth device * @param contacts Uri[] of the contacts * @param message Message to be sent * @param sentIntent intent issued when message is sent * @param deliveredIntent intent issued when message is delivered * @return true if the message is enqueued, false on error + * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message, @@ -410,6 +479,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * * @param device Bluetooth device * @return true if the message is enqueued, false on error + * @hide */ public boolean getUnreadMessages(BluetoothDevice device) { if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")"); @@ -431,6 +501,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @param device The Bluetooth device to get this value for. * @return Returns true if the Uploading bit value in SDP record's * MapSupportedFeatures field is set. False is returned otherwise. + * @hide */ public boolean isUploadingSupported(BluetoothDevice device) { final IBluetoothMapClient service = getService(); @@ -457,7 +528,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * "read", <code>UNDELETED</code> for "undeleted", <code>DELETED</code> for * "deleted", otherwise return error * @return <code>true</code> if request has been sent, <code>false</code> on error - * + * @hide */ @RequiresPermission(Manifest.permission.READ_SMS) public boolean setMessageStatus(BluetoothDevice device, String handle, int status) { diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index c31b04e81456..201d6c495d98 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -186,6 +186,7 @@ public interface BluetoothProfile { * * @hide */ + @SystemApi int MAP_CLIENT = 18; /** diff --git a/core/java/android/bluetooth/BufferConstraints.java b/core/java/android/bluetooth/BufferConstraints.java index 7e5ec1e78435..97d97232b7a6 100644 --- a/core/java/android/bluetooth/BufferConstraints.java +++ b/core/java/android/bluetooth/BufferConstraints.java @@ -90,7 +90,7 @@ public final class BufferConstraints implements Parcelable { * @hide */ @SystemApi - public @Nullable BufferConstraint getCodec(@BluetoothCodecConfig.SourceCodecType int codec) { + public @Nullable BufferConstraint forCodec(@BluetoothCodecConfig.SourceCodecType int codec) { return mBufferConstraints.get(codec); } } diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 46d8900e59a1..230c985d1dc8 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -132,8 +132,11 @@ public abstract class ContentResolver implements ContentInterface { public static final String SYNC_EXTRAS_ACCOUNT = "account"; /** - * If this extra is set to true, the sync request will be scheduled - * at the front of the sync request queue and without any delay + * If this extra is set to true, the sync request will be scheduled at the front of the + * sync request queue, but it is still subject to JobScheduler quota and throttling due to + * App Standby buckets. + * + * <p>This is different from {@link #SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB}. */ public static final String SYNC_EXTRAS_EXPEDITED = "expedited"; @@ -145,6 +148,29 @@ public abstract class ContentResolver implements ContentInterface { public static final String SYNC_EXTRAS_REQUIRE_CHARGING = "require_charging"; /** + * Run this sync operation as an "expedited job" + * (see {@link android.app.job.JobInfo.Builder#setExpedited(boolean)}). + * Normally (if this flag isn't specified), sync operations are executed as regular + * {@link android.app.job.JobService} jobs. + * + * <p> Because Expedited Jobs have various restrictions compared to regular jobs, this flag + * cannot be combined with certain other flags, otherwise an + * <code>IllegalArgumentException</code> will be thrown. Notably, because Expedited Jobs do not + * support various constraints, the following restriction apply: + * <ul> + * <li>Can't be used with {@link #SYNC_EXTRAS_REQUIRE_CHARGING} + * <li>Can't be used with {@link #SYNC_EXTRAS_EXPEDITED} + * <li>Can't be used on periodic syncs. + * <li>When an expedited-job-sync fails and a retry is scheduled, the retried sync will be + * scheduled as a regular job unless {@link #SYNC_EXTRAS_IGNORE_BACKOFF} is set. + * </ul> + * + * <p>This is different from {@link #SYNC_EXTRAS_EXPEDITED}. + */ + @SuppressLint("IntentName") + public static final String SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB = "schedule_as_expedited_job"; + + /** * @deprecated instead use * {@link #SYNC_EXTRAS_MANUAL} */ @@ -3220,6 +3246,18 @@ public abstract class ContentResolver implements ContentInterface { } /** + * {@hide} + * Helper function to throw an <code>IllegalArgumentException</code> if any illegal + * extras were set for a sync scheduled as an expedited job. + * + * @param extras bundle to validate. + */ + public static boolean hasInvalidScheduleAsEjExtras(Bundle extras) { + return extras.getBoolean(ContentResolver.SYNC_EXTRAS_REQUIRE_CHARGING) + || extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED); + } + + /** * Specifies that a sync should be requested with the specified the account, authority, * and extras at the given frequency. If there is already another periodic sync scheduled * with the account, authority and extras then a new periodic sync won't be added, instead @@ -3233,7 +3271,8 @@ public abstract class ContentResolver implements ContentInterface { * Periodic syncs are not allowed to have any of {@link #SYNC_EXTRAS_DO_NOT_RETRY}, * {@link #SYNC_EXTRAS_IGNORE_BACKOFF}, {@link #SYNC_EXTRAS_IGNORE_SETTINGS}, * {@link #SYNC_EXTRAS_INITIALIZE}, {@link #SYNC_EXTRAS_FORCE}, - * {@link #SYNC_EXTRAS_EXPEDITED}, {@link #SYNC_EXTRAS_MANUAL} set to true. + * {@link #SYNC_EXTRAS_EXPEDITED}, {@link #SYNC_EXTRAS_MANUAL}, + * {@link #SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB} set to true. * If any are supplied then an {@link IllegalArgumentException} will be thrown. * * <p>This method requires the caller to hold the permission @@ -3273,16 +3312,14 @@ public abstract class ContentResolver implements ContentInterface { * @param extras bundle to validate. */ public static boolean invalidPeriodicExtras(Bundle extras) { - if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false) + return extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false) || extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false) || extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false) || extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false) || extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false) || extras.getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false) - || extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) { - return true; - } - return false; + || extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false) + || extras.getBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, false); } /** diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 4284dc2117c5..f3a4e1f79955 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -370,6 +370,25 @@ public abstract class Context { /*********** Hidden flags below this line ***********/ /** + * Flag for {@link #bindService}: allow the process hosting the target service to be treated + * as if it's as important as a perceptible app to the user and avoid the oom killer killing + * this process in low memory situations until there aren't any other processes left but the + * ones which are user-perceptible. + * + * @hide + */ + public static final int BIND_ALMOST_PERCEPTIBLE = 0x000010000; + + /** + * Flag for {@link #bindService}: allow the process hosting the target service to gain + * {@link ActivityManager#PROCESS_CAPABILITY_NETWORK}, which allows it be able + * to access network regardless of any power saving restrictions. + * + * @hide + */ + public static final int BIND_ALLOW_NETWORK_ACCESS = 0x00020000; + + /** * Flag for {@link #bindService}: allow background foreground service starts from the bound * service's process. * This flag is only respected if the caller is holding @@ -3118,8 +3137,18 @@ public abstract class Context { * * @throws SecurityException If the caller does not have permission to access the service * or the service can not be found. - * @throws IllegalStateException If the application is in a state where the service - * can not be started (such as not in the foreground in a state when services are allowed). + * @throws IllegalStateException + * Before Android {@link android.os.Build.VERSION_CODES#S}, + * if the application is in a state where the service + * can not be started (such as not in the foreground in a state when services are allowed), + * {@link IllegalStateException} was thrown. + * @throws android.app.BackgroundServiceStartNotAllowedException + * On Android {@link android.os.Build.VERSION_CODES#S} and later, + * if the application is in a state where the service + * can not be started (such as not in the foreground in a state when services are allowed), + * {@link android.app.BackgroundServiceStartNotAllowedException} is thrown + * This excemption extends {@link IllegalStateException}, so apps can + * use {@code catch (IllegalStateException)} to catch both. * * @see #stopService * @see #bindService @@ -3150,7 +3179,8 @@ public abstract class Context { * @throws SecurityException If the caller does not have permission to access the service * or the service can not be found. * - * @throws IllegalStateException If the caller app's targeting API is + * @throws android.app.ForegroundServiceStartNotAllowedException + * If the caller app's targeting API is * {@link android.os.Build.VERSION_CODES#S} or later, and the foreground service is restricted * from start due to background restriction. * @@ -6663,15 +6693,6 @@ public abstract class Context { } /** - * Indicates if this context is a visual context such as {@link android.app.Activity} or - * a context created from {@link #createWindowContext(int, Bundle)}. - * @hide - */ - public boolean isUiContext() { - throw new RuntimeException("Not implemented. Must override in a subclass."); - } - - /** * Returns {@code true} if the context is a UI context which can access UI components such as * {@link WindowManager}, {@link android.view.LayoutInflater LayoutInflater} or * {@link android.app.WallpaperManager WallpaperManager}. Accessing UI components from non-UI @@ -6683,12 +6704,16 @@ public abstract class Context { * {@link #createWindowContext(int, Bundle)} or * {@link android.inputmethodservice.InputMethodService InputMethodService} * </p> + * <p> + * Note that even if it is allowed programmatically, it is not suggested to override this + * method to bypass {@link android.os.strictmode.IncorrectContextUseViolation} verification. + * </p> * * @see #getDisplay() * @see #getSystemService(String) * @see android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse() */ - public static boolean isUiContext(@NonNull Context context) { - return context.isUiContext(); + public boolean isUiContext() { + throw new RuntimeException("Not implemented. Must override in a subclass."); } } diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java index 858d1e498783..b1252fd0b21f 100644 --- a/core/java/android/content/IntentSender.java +++ b/core/java/android/content/IntentSender.java @@ -18,6 +18,7 @@ package android.content; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityManager.PendingIntentInfo; import android.compat.annotation.UnsupportedAppUsage; import android.os.Bundle; import android.os.Handler; @@ -60,6 +61,9 @@ public class IntentSender implements Parcelable { private final IIntentSender mTarget; IBinder mWhitelistToken; + // cached pending intent information + private @Nullable PendingIntentInfo mCachedInfo; + /** * Exception thrown when trying to send through a PendingIntent that * has been canceled or is otherwise no longer able to execute the request. @@ -209,13 +213,7 @@ public class IntentSender implements Parcelable { */ @Deprecated public String getTargetPackage() { - try { - return ActivityManager.getService() - .getPackageForIntentSender(mTarget); - } catch (RemoteException e) { - // Should never happen. - return null; - } + return getCreatorPackage(); } /** @@ -228,13 +226,7 @@ public class IntentSender implements Parcelable { * none associated with it. */ public String getCreatorPackage() { - try { - return ActivityManager.getService() - .getPackageForIntentSender(mTarget); - } catch (RemoteException e) { - // Should never happen. - return null; - } + return getCachedInfo().getCreatorPackage(); } /** @@ -247,13 +239,7 @@ public class IntentSender implements Parcelable { * none associated with it. */ public int getCreatorUid() { - try { - return ActivityManager.getService() - .getUidForIntentSender(mTarget); - } catch (RemoteException e) { - // Should never happen. - return -1; - } + return getCachedInfo().getCreatorUid(); } /** @@ -268,14 +254,8 @@ public class IntentSender implements Parcelable { * none associated with it. */ public UserHandle getCreatorUserHandle() { - try { - int uid = ActivityManager.getService() - .getUidForIntentSender(mTarget); - return uid > 0 ? new UserHandle(UserHandle.getUserId(uid)) : null; - } catch (RemoteException e) { - // Should never happen. - return null; - } + int uid = getCachedInfo().getCreatorUid(); + return uid > 0 ? new UserHandle(UserHandle.getUserId(uid)) : null; } /** @@ -384,4 +364,16 @@ public class IntentSender implements Parcelable { public IntentSender(IBinder target) { mTarget = IIntentSender.Stub.asInterface(target); } + + private PendingIntentInfo getCachedInfo() { + if (mCachedInfo == null) { + try { + mCachedInfo = ActivityManager.getService().getInfoForIntentSender(mTarget); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + return mCachedInfo; + } } diff --git a/core/java/android/content/SyncRequest.java b/core/java/android/content/SyncRequest.java index 9e568a40e0ee..e1e6f75d152f 100644 --- a/core/java/android/content/SyncRequest.java +++ b/core/java/android/content/SyncRequest.java @@ -17,6 +17,7 @@ package android.content; import android.accounts.Account; +import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Bundle; @@ -58,6 +59,8 @@ public class SyncRequest implements Parcelable { private final boolean mIsAuthority; /** Sync should be run in lieu of other syncs. */ private final boolean mIsExpedited; + /** Sync sound be ran as an expedited job. */ + private final boolean mIsScheduledAsExpeditedJob; /** * {@hide} @@ -79,6 +82,14 @@ public class SyncRequest implements Parcelable { /** * {@hide} + * @return whether this sync is scheduled as an expedited job. + */ + public boolean isScheduledAsExpeditedJob() { + return mIsScheduledAsExpeditedJob; + } + + /** + * {@hide} * * @return account object for this sync. * @throws IllegalArgumentException if this function is called for a request that targets a @@ -149,6 +160,7 @@ public class SyncRequest implements Parcelable { parcel.writeInt((mDisallowMetered ? 1 : 0)); parcel.writeInt((mIsAuthority ? 1 : 0)); parcel.writeInt((mIsExpedited? 1 : 0)); + parcel.writeInt(mIsScheduledAsExpeditedJob ? 1 : 0); parcel.writeParcelable(mAccountToSync, flags); parcel.writeString(mAuthority); } @@ -161,6 +173,7 @@ public class SyncRequest implements Parcelable { mDisallowMetered = (in.readInt() != 0); mIsAuthority = (in.readInt() != 0); mIsExpedited = (in.readInt() != 0); + mIsScheduledAsExpeditedJob = (in.readInt() != 0); mAccountToSync = in.readParcelable(null); mAuthority = in.readString(); } @@ -174,6 +187,7 @@ public class SyncRequest implements Parcelable { mIsPeriodic = (b.mSyncType == Builder.SYNC_TYPE_PERIODIC); mIsAuthority = (b.mSyncTarget == Builder.SYNC_TARGET_ADAPTER); mIsExpedited = b.mExpedited; + mIsScheduledAsExpeditedJob = b.mScheduleAsExpeditedJob; mExtras = new Bundle(b.mCustomExtras); // For now we merge the sync config extras & the custom extras into one bundle. // TODO: pass the configuration extras through separately. @@ -258,6 +272,11 @@ public class SyncRequest implements Parcelable { */ private boolean mRequiresCharging; + /** + * Whether the sync should be scheduled as an expedited job. + */ + private boolean mScheduleAsExpeditedJob; + public Builder() { } @@ -309,7 +328,8 @@ public class SyncRequest implements Parcelable { * {@link ContentResolver#SYNC_EXTRAS_INITIALIZE}, * {@link ContentResolver#SYNC_EXTRAS_FORCE}, * {@link ContentResolver#SYNC_EXTRAS_EXPEDITED}, - * {@link ContentResolver#SYNC_EXTRAS_MANUAL} + * {@link ContentResolver#SYNC_EXTRAS_MANUAL}, + * {@link ContentResolver#SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB} * set to true. If any are supplied then an <code>IllegalArgumentException</code> will * be thrown. * @@ -500,6 +520,22 @@ public class SyncRequest implements Parcelable { } /** + * Convenience function for setting + * {@link ContentResolver#SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB}. + * + * <p> Not to be confused with {@link ContentResolver#SYNC_EXTRAS_EXPEDITED}. + * + * <p> Not valid for periodic syncs, expedited syncs, and syncs that require charging - an + * <code>IllegalArgumentException</code> will be thrown in {@link #build()}. + * + * @param scheduleAsExpeditedJob whether to schedule as an expedited job. Default false. + */ + public @NonNull Builder setScheduleAsExpeditedJob(boolean scheduleAsExpeditedJob) { + mScheduleAsExpeditedJob = scheduleAsExpeditedJob; + return this; + } + + /** * Performs validation over the request and throws the runtime exception * <code>IllegalArgumentException</code> if this validation fails. * @@ -507,11 +543,6 @@ public class SyncRequest implements Parcelable { * builder. */ public SyncRequest build() { - // Validate the extras bundle - ContentResolver.validateSyncExtrasBundle(mCustomExtras); - if (mCustomExtras == null) { - mCustomExtras = new Bundle(); - } // Combine builder extra flags into the config bundle. mSyncConfigExtras = new Bundle(); if (mIgnoreBackoff) { @@ -532,17 +563,35 @@ public class SyncRequest implements Parcelable { if (mExpedited) { mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); } + if (mScheduleAsExpeditedJob) { + mSyncConfigExtras.putBoolean( + ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, true); + } if (mIsManual) { mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true); mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true); } + + if (mCustomExtras == null) { + mCustomExtras = new Bundle(); + } + // Validate the extras bundles + ContentResolver.validateSyncExtrasBundle(mCustomExtras); + // If this is a periodic sync ensure than invalid extras were not set. if (mSyncType == SYNC_TYPE_PERIODIC) { - // If this is a periodic sync ensure than invalid extras were not set. if (ContentResolver.invalidPeriodicExtras(mCustomExtras) || ContentResolver.invalidPeriodicExtras(mSyncConfigExtras)) { throw new IllegalArgumentException("Illegal extras were set"); } } + // If this sync is scheduled as an EJ, ensure that invalid extras were not set. + if (mCustomExtras.getBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB) + || mScheduleAsExpeditedJob) { + if (ContentResolver.hasInvalidScheduleAsEjExtras(mCustomExtras) + || ContentResolver.hasInvalidScheduleAsEjExtras(mSyncConfigExtras)) { + throw new IllegalArgumentException("Illegal extras were set"); + } + } // Ensure that a target for the sync has been set. if (mSyncTarget == SYNC_TARGET_UNKNOWN) { throw new IllegalArgumentException("Must specify an adapter with" + diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index dec2c3d7fe48..0aa1be94d279 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -136,6 +136,18 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public int fullBackupContent = 0; /** + * Applications can set this attribute to an xml resource within their app where they specified + * the rules determining which files and directories can be copied from the device as part of + * backup or transfer operations. + *<p> + * Set from the {@link android.R.styleable#AndroidManifestApplication_dataExtractionRules} + * attribute in the manifest. + * + * @hide + */ + public int dataExtractionRulesRes = 0; + + /** * <code>true</code> if the package is capable of presenting a unified interface representing * multiple profiles. * @hide @@ -1520,6 +1532,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { pw.println(prefix + "fullBackupContent=" + (fullBackupContent < 0 ? "false" : "true")); } + if (dataExtractionRulesRes != 0) { + pw.println(prefix + "dataExtractionRules=@xml/" + dataExtractionRulesRes); + } pw.println(prefix + "crossProfile=" + (crossProfile ? "true" : "false")); if (networkSecurityConfigRes != 0) { pw.println(prefix + "networkSecurityConfigRes=0x" @@ -1749,6 +1764,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { uiOptions = orig.uiOptions; backupAgentName = orig.backupAgentName; fullBackupContent = orig.fullBackupContent; + dataExtractionRulesRes = orig.dataExtractionRulesRes; crossProfile = orig.crossProfile; networkSecurityConfigRes = orig.networkSecurityConfigRes; category = orig.category; @@ -1836,6 +1852,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeInt(descriptionRes); dest.writeInt(uiOptions); dest.writeInt(fullBackupContent); + dest.writeInt(dataExtractionRulesRes); dest.writeBoolean(crossProfile); dest.writeInt(networkSecurityConfigRes); dest.writeInt(category); @@ -1920,6 +1937,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { descriptionRes = source.readInt(); uiOptions = source.readInt(); fullBackupContent = source.readInt(); + dataExtractionRulesRes = source.readInt(); crossProfile = source.readBoolean(); networkSecurityConfigRes = source.readInt(); category = source.readInt(); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index f9122b1f6c8b..9a3fbdcea9ff 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3601,7 +3601,7 @@ public abstract class PackageManager { * 1 - IncFs v1, core features, no PerUid support. Optional in R. * 2 - IncFs v2, PerUid support, fs-verity support. Required in S. * - * @see IncrementalManager#isFeatureEnabled and IncrementalManager#isV2() + * @see IncrementalManager#getVersion() * @hide */ @SystemApi diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java index 7a01392a24e8..29edd405be6b 100644 --- a/core/java/android/content/pm/parsing/ParsingPackage.java +++ b/core/java/android/content/pm/parsing/ParsingPackage.java @@ -260,6 +260,8 @@ public interface ParsingPackage extends ParsingPackageRead { ParsingPackage setFullBackupContent(int fullBackupContent); + ParsingPackage setDataExtractionRules(int dataExtractionRules); + ParsingPackage setHasDomainUrls(boolean hasDomainUrls); ParsingPackage setIconRes(int iconRes); diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java index c1a93d8c2428..067787d725d9 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java +++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java @@ -334,6 +334,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { private int descriptionRes; private int fullBackupContent; + private int dataExtractionRules; private int iconRes; private int installLocation = ParsingPackageUtils.PARSE_DEFAULT_INSTALL_LOCATION; private int labelRes; @@ -1015,6 +1016,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { appInfo.enabled = getBoolean(Booleans.ENABLED); // appInfo.enabledSetting appInfo.fullBackupContent = fullBackupContent; + appInfo.dataExtractionRulesRes = dataExtractionRules; // TODO(b/135203078): See ParsingPackageImpl#getHiddenApiEnforcementPolicy // appInfo.mHiddenApiPolicy // appInfo.hiddenUntilInstalled @@ -1163,6 +1165,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { dest.writeInt(this.compatibleWidthLimitDp); dest.writeInt(this.descriptionRes); dest.writeInt(this.fullBackupContent); + dest.writeInt(this.dataExtractionRules); dest.writeInt(this.iconRes); dest.writeInt(this.installLocation); dest.writeInt(this.labelRes); @@ -1284,6 +1287,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { this.compatibleWidthLimitDp = in.readInt(); this.descriptionRes = in.readInt(); this.fullBackupContent = in.readInt(); + this.dataExtractionRules = in.readInt(); this.iconRes = in.readInt(); this.installLocation = in.readInt(); this.labelRes = in.readInt(); @@ -1808,6 +1812,11 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { } @Override + public int getDataExtractionRules() { + return dataExtractionRules; + } + + @Override public int getIconRes() { return iconRes; } @@ -2264,6 +2273,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { } @Override + public ParsingPackageImpl setDataExtractionRules(int value) { + dataExtractionRules = value; + return this; + } + + @Override public ParsingPackageImpl setIconRes(int value) { iconRes = value; return this; diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java index ff4cebdd1533..f7f3e19efdf3 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageRead.java +++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java @@ -587,6 +587,11 @@ public interface ParsingPackageRead extends Parcelable { */ int getFullBackupContent(); + /** + * @see R.styleable#AndroidManifestApplication_dataExtractionRules + */ + int getDataExtractionRules(); + /** @see ApplicationInfo#PRIVATE_FLAG_HAS_DOMAIN_URLS */ boolean isHasDomainUrls(); diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index b7aa30f00691..0c033fddf069 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -2168,6 +2168,8 @@ public class ParsingPackageUtils { .setNetworkSecurityConfigRes(resId(R.styleable.AndroidManifestApplication_networkSecurityConfig, sa)) .setRoundIconRes(resId(R.styleable.AndroidManifestApplication_roundIcon, sa)) .setTheme(resId(R.styleable.AndroidManifestApplication_theme, sa)) + .setDataExtractionRules( + resId(R.styleable.AndroidManifestApplication_dataExtractionRules, sa)) // Strings .setClassLoaderName(string(R.styleable.AndroidManifestApplication_classLoader, sa)) .setRequiredAccountType(string(R.styleable.AndroidManifestApplication_requiredAccountType, sa)) diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java index 76d50bdf414c..43ef33e1f420 100644 --- a/core/java/android/hardware/biometrics/BiometricConstants.java +++ b/core/java/android/hardware/biometrics/BiometricConstants.java @@ -145,6 +145,12 @@ public interface BiometricConstants { int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15; /** + * Authentication cannot proceed because re-enrollment is required. + * @hide + */ + int BIOMETRIC_ERROR_RE_ENROLL = 16; + + /** * This constant is only used by SystemUI. It notifies SystemUI that authentication was paused * because the authentication attempt was unsuccessful. * @hide diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java index eafcf529de62..4385b1dac7a0 100644 --- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java +++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java @@ -153,6 +153,12 @@ public interface BiometricFaceConstants { int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15; /** + * Authentication cannot proceed because re-enrollment is required. + * @hide + */ + int BIOMETRIC_ERROR_RE_ENROLL = 16; + + /** * @hide */ int FACE_ERROR_VENDOR_BASE = 1000; diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java index 01f0e71a7c33..30e24d2ec8db 100644 --- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java +++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java @@ -166,6 +166,12 @@ public interface BiometricFingerprintConstants { public static final int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15; /** + * Authentication cannot proceed because re-enrollment is required. + * @hide + */ + int BIOMETRIC_ERROR_RE_ENROLL = 16; + + /** * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index f9eecaec5bb2..ac6ba0a4ac58 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -378,9 +378,10 @@ public abstract class CameraDevice implements AutoCloseable { * released, continuous repeating requests stopped and any pending * multi-frame capture requests flushed.</p> * - * <p>Note that the CameraExtensionSession currently supports at most two - * multi frame capture surface formats: ImageFormat.YUV_420_888 and - * ImageFormat.JPEG. Clients must query the multi-frame capture format support using + * <p>Note that the CameraExtensionSession currently supports at most wo + * multi frame capture surface formats: ImageFormat.JPEG will be supported + * by all extensions and ImageFormat.YUV_420_888 may or may not be supported. + * Clients must query the multi-frame capture format support using * {@link CameraExtensionCharacteristics#getExtensionSupportedSizes(int, int)}. * For repeating requests CameraExtensionSession supports only * {@link android.graphics.SurfaceTexture} as output. Clients can query the supported resolution diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java index d3eb3779189b..6121cd260dc3 100644 --- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java @@ -35,6 +35,7 @@ import android.util.Log; import android.util.Pair; import android.util.Size; +import java.util.HashSet; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -153,12 +154,8 @@ public final class CameraExtensionCharacteristics { mChars = chars; } - private static List<Size> generateSupportedSizes(List<SizeList> sizesList, - Integer format, - StreamConfigurationMap streamMap) { - // Per API contract it is assumed that the extension is able to support all - // camera advertised sizes for a given format in case it doesn't return - // a valid non-empty size list. + private static ArrayList<Size> getSupportedSizes(List<SizeList> sizesList, + Integer format) { ArrayList<Size> ret = new ArrayList<>(); if ((sizesList != null) && (!sizesList.isEmpty())) { for (SizeList entry : sizesList) { @@ -170,13 +167,36 @@ public final class CameraExtensionCharacteristics { } } } + + return ret; + } + + private static List<Size> generateSupportedSizes(List<SizeList> sizesList, + Integer format, + StreamConfigurationMap streamMap) { + // Per API contract it is assumed that the extension is able to support all + // camera advertised sizes for a given format in case it doesn't return + // a valid non-empty size list. + ArrayList<Size> ret = getSupportedSizes(sizesList, format); Size[] supportedSizes = streamMap.getOutputSizes(format); - if (supportedSizes != null) { + if ((ret.isEmpty()) && (supportedSizes != null)) { ret.addAll(Arrays.asList(supportedSizes)); } return ret; } + private static List<Size> generateJpegSupportedSizes(List<SizeList> sizesList, + StreamConfigurationMap streamMap) { + ArrayList<Size> extensionSizes = getSupportedSizes(sizesList, ImageFormat.YUV_420_888); + HashSet<Size> supportedSizes = extensionSizes.isEmpty() ? new HashSet<>(Arrays.asList( + streamMap.getOutputSizes(ImageFormat.YUV_420_888))) : new HashSet<>(extensionSizes); + HashSet<Size> supportedJpegSizes = new HashSet<>(Arrays.asList(streamMap.getOutputSizes( + ImageFormat.JPEG))); + supportedSizes.retainAll(supportedJpegSizes); + + return new ArrayList<>(supportedSizes); + } + /** * A per-process global camera extension manager instance, to track and * initialize/release extensions depending on client activity. @@ -488,8 +508,8 @@ public final class CameraExtensionCharacteristics { * {@link StreamConfigurationMap#getOutputSizes}.</p> * * <p>Device-specific extensions currently support at most two - * multi-frame capture surface formats, ImageFormat.YUV_420_888 or - * ImageFormat.JPEG.</p> + * multi-frame capture surface formats. ImageFormat.JPEG will be supported by all + * extensions and ImageFormat.YUV_420_888 may or may not be supported.</p> * * @param extension the extension type * @param format device-specific extension output format @@ -526,14 +546,17 @@ public final class CameraExtensionCharacteristics { format, streamMap); } else if (format == ImageFormat.JPEG) { extenders.second.init(mCameraId, mChars.getNativeMetadata()); - if (extenders.second.getCaptureProcessor() == null) { + if (extenders.second.getCaptureProcessor() != null) { + // The framework will perform the additional encoding pass on the + // processed YUV_420 buffers. + return generateJpegSupportedSizes( + extenders.second.getSupportedResolutions(), streamMap); + } else { return generateSupportedSizes(null, format, streamMap); } - - return new ArrayList<>(); + } else { + throw new IllegalArgumentException("Unsupported format: " + format); } - - throw new IllegalArgumentException("Unsupported format: " + format); } finally { unregisterClient(clientId); } diff --git a/core/java/android/hardware/camera2/CameraExtensionSession.java b/core/java/android/hardware/camera2/CameraExtensionSession.java index 877dfbc49df2..e1b817768461 100644 --- a/core/java/android/hardware/camera2/CameraExtensionSession.java +++ b/core/java/android/hardware/camera2/CameraExtensionSession.java @@ -238,8 +238,10 @@ public abstract class CameraExtensionSession implements AutoCloseable { * from the camera device, to produce a single high-quality output result. * * <p>Note that single capture requests currently do not support - * client parameters. Settings included in the request will - * be entirely overridden by the device-specific extension. </p> + * client parameters except for {@link CaptureRequest#JPEG_ORIENTATION orientation} and + * {@link CaptureRequest#JPEG_QUALITY quality} in case of ImageFormat.JPEG output target. + * The rest of the settings included in the request will be entirely overridden by + * the device-specific extension. </p> * * <p>The {@link CaptureRequest.Builder#addTarget} supports only one * ImageFormat.YUV_420_888 or ImageFormat.JPEG target surface. {@link CaptureRequest} diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java new file mode 100644 index 000000000000..936734b0c711 --- /dev/null +++ b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java @@ -0,0 +1,312 @@ +/* + * 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.hardware.camera2.impl; + +import android.annotation.NonNull; +import android.graphics.ImageFormat; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.extension.CaptureBundle; +import android.hardware.camera2.extension.ICaptureProcessorImpl; +import android.media.Image; +import android.media.Image.Plane; +import android.media.ImageReader; +import android.media.ImageWriter; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; +import android.view.Surface; + +import java.nio.ByteBuffer; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; + +// Jpeg compress input YUV and queue back in the client target surface. +public class CameraExtensionJpegProcessor implements ICaptureProcessorImpl { + public final static String TAG = "CameraExtensionJpeg"; + private final static int JPEG_QUEUE_SIZE = 1; + private final static int JPEG_DEFAULT_QUALITY = 100; + private final static int JPEG_DEFAULT_ROTATION = 0; + + private final Handler mHandler; + private final HandlerThread mHandlerThread; + private final ICaptureProcessorImpl mProcessor; + + private ImageReader mYuvReader = null; + private android.hardware.camera2.extension.Size mResolution = null; + private int mFormat = -1; + private Surface mOutputSurface = null; + private ImageWriter mOutputWriter = null; + + private static final class JpegParameters { + public HashSet<Long> mTimeStamps = new HashSet<>(); + public int mRotation = JPEG_DEFAULT_ROTATION; // CCW multiple of 90 degrees + public int mQuality = JPEG_DEFAULT_QUALITY; // [0..100] + } + + private ConcurrentLinkedQueue<JpegParameters> mJpegParameters = new ConcurrentLinkedQueue<>(); + + public CameraExtensionJpegProcessor(@NonNull ICaptureProcessorImpl processor) { + mProcessor = processor; + mHandlerThread = new HandlerThread(TAG); + mHandlerThread.start(); + mHandler = new Handler(mHandlerThread.getLooper()); + } + + public void close() { + mHandlerThread.quitSafely(); + + if (mOutputWriter != null) { + mOutputWriter.close(); + mOutputWriter = null; + } + + if (mYuvReader != null) { + mYuvReader.close(); + mYuvReader = null; + } + } + + private static JpegParameters getJpegParameters(List<CaptureBundle> captureBundles) { + JpegParameters ret = new JpegParameters(); + if (!captureBundles.isEmpty()) { + // The quality and orientation settings must be equal for requests in a burst + + Byte jpegQuality = captureBundles.get(0).captureResult.get(CaptureResult.JPEG_QUALITY); + if (jpegQuality != null) { + ret.mQuality = jpegQuality; + } else { + Log.w(TAG, "No jpeg quality set, using default: " + JPEG_DEFAULT_QUALITY); + } + + Integer orientation = captureBundles.get(0).captureResult.get( + CaptureResult.JPEG_ORIENTATION); + if (orientation != null) { + ret.mRotation = orientation / 90; + } else { + Log.w(TAG, "No jpeg rotation set, using default: " + JPEG_DEFAULT_ROTATION); + } + + for (CaptureBundle bundle : captureBundles) { + Long timeStamp = bundle.captureResult.get(CaptureResult.SENSOR_TIMESTAMP); + if (timeStamp != null) { + ret.mTimeStamps.add(timeStamp); + } else { + Log.e(TAG, "Capture bundle without valid sensor timestamp!"); + } + } + } + + return ret; + } + + /** + * Compresses a YCbCr image to jpeg, applying a crop and rotation. + * <p> + * The input is defined as a set of 3 planes of 8-bit samples, one plane for + * each channel of Y, Cb, Cr.<br> + * The Y plane is assumed to have the same width and height of the entire + * image.<br> + * The Cb and Cr planes are assumed to be downsampled by a factor of 2, to + * have dimensions (floor(width / 2), floor(height / 2)).<br> + * Each plane is specified by a direct java.nio.ByteBuffer, a pixel-stride, + * and a row-stride. So, the sample at coordinate (x, y) can be retrieved + * from byteBuffer[x * pixel_stride + y * row_stride]. + * <p> + * The pre-compression transformation is applied as follows: + * <ol> + * <li>The image is cropped to the rectangle from (cropLeft, cropTop) to + * (cropRight - 1, cropBottom - 1). So, a cropping-rectangle of (0, 0) - + * (width, height) is a no-op.</li> + * <li>The rotation is applied counter-clockwise relative to the coordinate + * space of the image, so a CCW rotation will appear CW when the image is + * rendered in scanline order. Only rotations which are multiples of + * 90-degrees are suppored, so the parameter 'rot90' specifies which + * multiple of 90 to rotate the image.</li> + * </ol> + * + * @param width the width of the image to compress + * @param height the height of the image to compress + * @param yBuf the buffer containing the Y component of the image + * @param yPStride the stride between adjacent pixels in the same row in + * yBuf + * @param yRStride the stride between adjacent rows in yBuf + * @param cbBuf the buffer containing the Cb component of the image + * @param cbPStride the stride between adjacent pixels in the same row in + * cbBuf + * @param cbRStride the stride between adjacent rows in cbBuf + * @param crBuf the buffer containing the Cr component of the image + * @param crPStride the stride between adjacent pixels in the same row in + * crBuf + * @param crRStride the stride between adjacent rows in crBuf + * @param outBuf a direct java.nio.ByteBuffer to hold the compressed jpeg. + * This must have enough capacity to store the result, or an + * error code will be returned. + * @param outBufCapacity the capacity of outBuf + * @param quality the jpeg-quality (1-100) to use + * @param cropLeft left-edge of the bounds of the image to crop to before + * rotation + * @param cropTop top-edge of the bounds of the image to crop to before + * rotation + * @param cropRight right-edge of the bounds of the image to crop to before + * rotation + * @param cropBottom bottom-edge of the bounds of the image to crop to + * before rotation + * @param rot90 the multiple of 90 to rotate the image CCW (after cropping) + */ + private static native int compressJpegFromYUV420pNative( + int width, int height, + ByteBuffer yBuf, int yPStride, int yRStride, + ByteBuffer cbBuf, int cbPStride, int cbRStride, + ByteBuffer crBuf, int crPStride, int crRStride, + ByteBuffer outBuf, int outBufCapacity, + int quality, + int cropLeft, int cropTop, int cropRight, int cropBottom, + int rot90); + + public void process(List<CaptureBundle> captureBundle) throws RemoteException { + JpegParameters jpegParams = getJpegParameters(captureBundle); + try { + mJpegParameters.add(jpegParams); + mProcessor.process(captureBundle); + } catch (Exception e) { + mJpegParameters.remove(jpegParams); + throw e; + } + } + + public void onOutputSurface(Surface surface, int format) throws RemoteException { + if (format != ImageFormat.JPEG) { + Log.e(TAG, "Unsupported output format: " + format); + return; + } + mOutputSurface = surface; + initializePipeline(); + } + + @Override + public void onResolutionUpdate(android.hardware.camera2.extension.Size size) + throws RemoteException { + mResolution = size; + initializePipeline(); + } + + public void onImageFormatUpdate(int format) throws RemoteException { + if (format != ImageFormat.YUV_420_888) { + Log.e(TAG, "Unsupported input format: " + format); + return; + } + mFormat = format; + initializePipeline(); + } + + private void initializePipeline() throws RemoteException { + if ((mFormat != -1) && (mOutputSurface != null) && (mResolution != null) && + (mYuvReader == null)) { + // Jpeg/blobs are expected to be configured with (w*h)x1 + mOutputWriter = ImageWriter.newInstance(mOutputSurface, 1 /*maxImages*/, + ImageFormat.JPEG, mResolution.width * mResolution.height, 1); + mYuvReader = ImageReader.newInstance(mResolution.width, mResolution.height, mFormat, + JPEG_QUEUE_SIZE); + mYuvReader.setOnImageAvailableListener(new YuvCallback(), mHandler); + mProcessor.onOutputSurface(mYuvReader.getSurface(), mFormat); + mProcessor.onResolutionUpdate(mResolution); + mProcessor.onImageFormatUpdate(mFormat); + } + } + + @Override + public IBinder asBinder() { + throw new UnsupportedOperationException("Binder IPC not supported!"); + } + + private class YuvCallback implements ImageReader.OnImageAvailableListener { + @Override + public void onImageAvailable(ImageReader reader) { + Image yuvImage = null; + Image jpegImage = null; + try { + yuvImage = mYuvReader.acquireNextImage(); + jpegImage = mOutputWriter.dequeueInputImage(); + } catch (IllegalStateException e) { + if (yuvImage != null) { + yuvImage.close(); + } + if (jpegImage != null) { + jpegImage.close(); + } + Log.e(TAG, "Failed to acquire processed yuv image or jpeg image!"); + return; + } + + ByteBuffer jpegBuffer = jpegImage.getPlanes()[0].getBuffer(); + jpegBuffer.clear(); + // Jpeg/blobs are expected to be configured with (w*h)x1 + int jpegCapacity = jpegImage.getWidth(); + + Plane lumaPlane = yuvImage.getPlanes()[0]; + Plane crPlane = yuvImage.getPlanes()[1]; + Plane cbPlane = yuvImage.getPlanes()[2]; + + Iterator<JpegParameters> jpegIter = mJpegParameters.iterator(); + JpegParameters jpegParams = null; + while(jpegIter.hasNext()) { + JpegParameters currentParams = jpegIter.next(); + if (currentParams.mTimeStamps.contains(yuvImage.getTimestamp())) { + jpegParams = currentParams; + jpegIter.remove(); + break; + } + } + if (jpegParams == null) { + if (mJpegParameters.isEmpty()) { + Log.w(TAG, "Empty jpeg settings queue! Using default jpeg orientation" + + " and quality!"); + jpegParams = new JpegParameters(); + jpegParams.mRotation = JPEG_DEFAULT_ROTATION; + jpegParams.mQuality = JPEG_DEFAULT_QUALITY; + } else { + Log.w(TAG, "No jpeg settings found with matching timestamp for current" + + " processed input!"); + Log.w(TAG, "Using values from the top of the queue!"); + jpegParams = mJpegParameters.poll(); + } + } + + compressJpegFromYUV420pNative( + yuvImage.getWidth(), yuvImage.getHeight(), + lumaPlane.getBuffer(), lumaPlane.getPixelStride(), lumaPlane.getRowStride(), + crPlane.getBuffer(), crPlane.getPixelStride(), crPlane.getRowStride(), + cbPlane.getBuffer(), cbPlane.getPixelStride(), cbPlane.getRowStride(), + jpegBuffer, jpegCapacity, jpegParams.mQuality, + 0, 0, yuvImage.getWidth(), yuvImage.getHeight(), + jpegParams.mRotation); + yuvImage.close(); + + try { + mOutputWriter.queueInputImage(jpegImage); + } catch (IllegalStateException e) { + Log.e(TAG, "Failed to queue encoded result!"); + } finally { + jpegImage.close(); + } + } + } +} diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java index 8451dedb6c37..0a561716d076 100644 --- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java @@ -91,6 +91,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { private ImageReader mStubCaptureImageReader = null; private ImageWriter mRepeatingRequestImageWriter = null; + private CameraExtensionJpegProcessor mImageJpegProcessor = null; private ICaptureProcessorImpl mImageProcessor = null; private CameraExtensionForwardProcessor mPreviewImageProcessor = null; private IRequestUpdateProcessorImpl mPreviewRequestUpdateProcessor = null; @@ -413,6 +414,10 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { if (mImageProcessor != null) { if (mClientCaptureSurface != null) { SurfaceInfo surfaceInfo = querySurface(mClientCaptureSurface); + if (surfaceInfo.mFormat == ImageFormat.JPEG) { + mImageJpegProcessor = new CameraExtensionJpegProcessor(mImageProcessor); + mImageProcessor = mImageJpegProcessor; + } mBurstCaptureImageReader = ImageReader.newInstance(surfaceInfo.mWidth, surfaceInfo.mHeight, CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT, mImageExtender.getMaxCaptureStage()); @@ -570,14 +575,16 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { return null; } - // Set user supported jpeg quality and rotation parameters + // This will override the extension capture stage jpeg parameters with the user set + // jpeg quality and rotation. This will guarantee that client configured jpeg + // parameters always have highest priority. Integer jpegRotation = clientRequest.get(CaptureRequest.JPEG_ORIENTATION); if (jpegRotation != null) { - requestBuilder.set(CaptureRequest.JPEG_ORIENTATION, jpegRotation); + captureStage.parameters.set(CaptureRequest.JPEG_ORIENTATION, jpegRotation); } Byte jpegQuality = clientRequest.get(CaptureRequest.JPEG_QUALITY); if (jpegQuality != null) { - requestBuilder.set(CaptureRequest.JPEG_QUALITY, jpegQuality); + captureStage.parameters.set(CaptureRequest.JPEG_QUALITY, jpegQuality); } requestBuilder.addTarget(target); @@ -753,6 +760,11 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { mPreviewImageProcessor = null; } + if (mImageJpegProcessor != null) { + mImageJpegProcessor.close(); + mImageJpegProcessor = null; + } + mCaptureSession = null; mImageProcessor = null; mCameraRepeatingSurface = mClientRepeatingRequestSurface = null; @@ -1014,7 +1026,10 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { mCaptureRequestMap.clear(); mCapturePendingMap.clear(); boolean processStatus = true; - List<CaptureBundle> captureList = initializeParcelable(mCaptureStageMap); + Byte jpegQuality = mClientRequest.get(CaptureRequest.JPEG_QUALITY); + Integer jpegOrientation = mClientRequest.get(CaptureRequest.JPEG_ORIENTATION); + List<CaptureBundle> captureList = initializeParcelable(mCaptureStageMap, + jpegOrientation, jpegQuality); try { mImageProcessor.process(captureList); } catch (RemoteException e) { @@ -1444,10 +1459,8 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { } for (int i = idx; i >= 0; i--) { if (previewMap.valueAt(i).first != null) { - Log.w(TAG, "Discard pending buffer with timestamp: " + previewMap.keyAt(i)); previewMap.valueAt(i).first.close(); } else { - Log.w(TAG, "Discard pending result with timestamp: " + previewMap.keyAt(i)); if (mClientNotificationsEnabled && ((i != idx) || notifyCurrentIndex)) { Log.w(TAG, "Preview frame drop with timestamp: " + previewMap.keyAt(i)); final long ident = Binder.clearCallingIdentity(); @@ -1639,7 +1652,8 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { } private static List<CaptureBundle> initializeParcelable( - HashMap<Integer, Pair<Image, TotalCaptureResult>> captureMap) { + HashMap<Integer, Pair<Image, TotalCaptureResult>> captureMap, Integer jpegOrientation, + Byte jpegQuality) { ArrayList<CaptureBundle> ret = new ArrayList<>(); for (Integer stagetId : captureMap.keySet()) { Pair<Image, TotalCaptureResult> entry = captureMap.get(stagetId); @@ -1648,6 +1662,12 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { bundle.captureImage = initializeParcelImage(entry.first); bundle.sequenceId = entry.second.getSequenceId(); bundle.captureResult = entry.second.getNativeMetadata(); + if (jpegOrientation != null) { + bundle.captureResult.set(CaptureResult.JPEG_ORIENTATION, jpegOrientation); + } + if (jpegQuality != null) { + bundle.captureResult.set(CaptureResult.JPEG_QUALITY, jpegQuality); + } ret.add(bundle); } diff --git a/core/java/android/hardware/display/DeviceProductInfo.java b/core/java/android/hardware/display/DeviceProductInfo.java index 9457d8f1aac4..41126b70c89f 100644 --- a/core/java/android/hardware/display/DeviceProductInfo.java +++ b/core/java/android/hardware/display/DeviceProductInfo.java @@ -16,69 +16,40 @@ package android.hardware.display; -import android.annotation.IntDef; -import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; import java.util.Objects; /** * Product-specific information about the display or the directly connected device on the * display chain. For example, if the display is transitively connected, this field may contain * product information about the intermediate device. + * @hide */ public final class DeviceProductInfo implements Parcelable { - /** @hide */ - @IntDef(prefix = {"CONNECTION_TO_SINK_"}, value = { - CONNECTION_TO_SINK_UNKNOWN, - CONNECTION_TO_SINK_BUILT_IN, - CONNECTION_TO_SINK_DIRECT, - CONNECTION_TO_SINK_TRANSITIVE - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ConnectionToSinkType { } - - /** The device connection to the display sink is unknown. */ - public static final int CONNECTION_TO_SINK_UNKNOWN = - IDeviceProductInfoConstants.CONNECTION_TO_SINK_UNKNOWN; - - /** The display sink is built-in to the device */ - public static final int CONNECTION_TO_SINK_BUILT_IN = - IDeviceProductInfoConstants.CONNECTION_TO_SINK_BUILT_IN; - - /** The device is directly connected to the display sink. */ - public static final int CONNECTION_TO_SINK_DIRECT = - IDeviceProductInfoConstants.CONNECTION_TO_SINK_DIRECT; - - /** The device is transitively connected to the display sink. */ - public static final int CONNECTION_TO_SINK_TRANSITIVE = - IDeviceProductInfoConstants.CONNECTION_TO_SINK_TRANSITIVE; - private final String mName; private final String mManufacturerPnpId; private final String mProductId; private final Integer mModelYear; private final ManufactureDate mManufactureDate; - private final @ConnectionToSinkType int mConnectionToSinkType; + private final int[] mRelativeAddress; - /** @hide */ public DeviceProductInfo( String name, String manufacturerPnpId, String productId, Integer modelYear, ManufactureDate manufactureDate, - int connectionToSinkType) { + int[] relativeAddress) { this.mName = name; this.mManufacturerPnpId = manufacturerPnpId; this.mProductId = productId; this.mModelYear = modelYear; this.mManufactureDate = manufactureDate; - this.mConnectionToSinkType = connectionToSinkType; + this.mRelativeAddress = relativeAddress; } private DeviceProductInfo(Parcel in) { @@ -87,13 +58,12 @@ public final class DeviceProductInfo implements Parcelable { mProductId = (String) in.readValue(null); mModelYear = (Integer) in.readValue(null); mManufactureDate = (ManufactureDate) in.readValue(null); - mConnectionToSinkType = in.readInt(); + mRelativeAddress = in.createIntArray(); } /** * @return Display name. */ - @Nullable public String getName() { return mName; } @@ -101,7 +71,6 @@ public final class DeviceProductInfo implements Parcelable { /** * @return Manufacturer Plug and Play ID. */ - @NonNull public String getManufacturerPnpId() { return mManufacturerPnpId; } @@ -109,58 +78,32 @@ public final class DeviceProductInfo implements Parcelable { /** * @return Manufacturer product ID. */ - @NonNull public String getProductId() { return mProductId; } /** - * @return Model year of the device. Return -1 if not available. Typically, - * one of model year or manufacture year is available. + * @return Model year of the device. Typically exactly one of model year or + * manufacture date will be present. */ - public int getModelYear() { - return mModelYear != null ? mModelYear : -1; - } - - /** - * @return The year of manufacture, or -1 it is not available. Typically, - * one of model year or manufacture year is available. - */ - public int getManufactureYear() { - if (mManufactureDate == null) { - return -1; - } - return mManufactureDate.mYear != null ? mManufactureDate.mYear : -1; - } - - /** - * @return The week of manufacture, or -1 it is not available. Typically, - * not present if model year is available. - */ - public int getManufactureWeek() { - if (mManufactureDate == null) { - return -1; - } - return mManufactureDate.mWeek != null ? mManufactureDate.mWeek : -1; + public Integer getModelYear() { + return mModelYear; } /** * @return Manufacture date. Typically exactly one of model year or manufacture * date will be present. - * - * @hide */ public ManufactureDate getManufactureDate() { return mManufactureDate; } /** - * @return How the current device is connected to the display sink. For example, the display - * can be connected immediately to the device or there can be a receiver in between. + * @return Relative address in the display network. For example, for HDMI connected devices this + * can be its physical address. Each component of the address is in the range [0, 255]. */ - @ConnectionToSinkType - public int getConnectionToSinkType() { - return mConnectionToSinkType; + public int[] getRelativeAddress() { + return mRelativeAddress; } @Override @@ -176,8 +119,8 @@ public final class DeviceProductInfo implements Parcelable { + mModelYear + ", manufactureDate=" + mManufactureDate - + ", connectionToSinkType=" - + mConnectionToSinkType + + ", relativeAddress=" + + Arrays.toString(mRelativeAddress) + '}'; } @@ -191,16 +134,16 @@ public final class DeviceProductInfo implements Parcelable { && Objects.equals(mProductId, that.mProductId) && Objects.equals(mModelYear, that.mModelYear) && Objects.equals(mManufactureDate, that.mManufactureDate) - && mConnectionToSinkType == that.mConnectionToSinkType; + && Arrays.equals(mRelativeAddress, that.mRelativeAddress); } @Override public int hashCode() { return Objects.hash(mName, mManufacturerPnpId, mProductId, mModelYear, mManufactureDate, - mConnectionToSinkType); + Arrays.hashCode(mRelativeAddress)); } - @NonNull public static final Creator<DeviceProductInfo> CREATOR = + public static final Creator<DeviceProductInfo> CREATOR = new Creator<DeviceProductInfo>() { @Override public DeviceProductInfo createFromParcel(Parcel in) { @@ -219,13 +162,13 @@ public final class DeviceProductInfo implements Parcelable { } @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { + public void writeToParcel(Parcel dest, int flags) { dest.writeString(mName); dest.writeString(mManufacturerPnpId); dest.writeValue(mProductId); dest.writeValue(mModelYear); dest.writeValue(mManufactureDate); - dest.writeInt(mConnectionToSinkType); + dest.writeIntArray(mRelativeAddress); } /** diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 48b05b7d9e15..2d58520a942e 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -67,12 +67,6 @@ public abstract class DisplayManagerInternal { public abstract boolean isProximitySensorAvailable(); /** - * Returns the id of the {@link com.android.server.display.DisplayGroup} to which the provided - * display belongs. - */ - public abstract int getDisplayGroupId(int displayId); - - /** * Registers a display group listener which will be informed of the addition, removal, or change * of display groups. * @@ -469,7 +463,7 @@ public abstract class DisplayManagerInternal { void onStateChanged(); void onProximityPositive(); void onProximityNegative(); - void onDisplayStateChange(int state); // one of the Display state constants + void onDisplayStateChange(boolean allInactive, boolean allOff); void acquireSuspendBlocker(); void releaseSuspendBlocker(); diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index a9bcdeff7e47..f868a5066c25 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -831,6 +831,9 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan case BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED: return context.getString( com.android.internal.R.string.face_error_security_update_required); + case BIOMETRIC_ERROR_RE_ENROLL: + return context.getString( + com.android.internal.R.string.face_recalibrate_notification_content); case FACE_ERROR_VENDOR: { String[] msgArray = context.getResources().getStringArray( com.android.internal.R.array.face_error_vendor); @@ -1389,7 +1392,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan case FACE_ACQUIRED_PAN_TOO_EXTREME: case FACE_ACQUIRED_TILT_TOO_EXTREME: case FACE_ACQUIRED_ROLL_TOO_EXTREME: - return context.getString(R.string.face_acquired_not_detected); + return context.getString(R.string.face_acquired_poor_gaze); // Provide more detailed feedback for other soft errors. case FACE_ACQUIRED_INSUFFICIENT: diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index eaa38f3e862c..4743fee3257b 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -25,6 +25,8 @@ import android.hardware.input.TouchCalibration; import android.os.CombinedVibrationEffect; import android.hardware.input.IInputSensorEventListener; import android.hardware.input.InputSensorInfo; +import android.hardware.lights.Light; +import android.hardware.lights.LightState; import android.os.IBinder; import android.os.IVibratorStateListener; import android.os.VibrationEffect; @@ -127,4 +129,14 @@ interface IInputManager { void disableSensor(int deviceId, int sensorType); boolean flushSensor(int deviceId, int sensorType); + + List<Light> getLights(int deviceId); + + LightState getLightState(int deviceId, int lightId); + + void setLightStates(int deviceId, in int[] lightIds, in LightState[] states, in IBinder token); + + void openLightSession(int deviceId, String opPkg, in IBinder token); + + void closeLightSession(int deviceId, in IBinder token); } diff --git a/core/java/android/hardware/input/InputDeviceLightsManager.java b/core/java/android/hardware/input/InputDeviceLightsManager.java new file mode 100644 index 000000000000..a3b91a99fdb7 --- /dev/null +++ b/core/java/android/hardware/input/InputDeviceLightsManager.java @@ -0,0 +1,139 @@ +/* + * 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.hardware.input; + +import android.annotation.NonNull; +import android.app.ActivityThread; +import android.hardware.lights.Light; +import android.hardware.lights.LightState; +import android.hardware.lights.LightsManager; +import android.hardware.lights.LightsRequest; +import android.util.CloseGuard; + +import com.android.internal.util.Preconditions; + +import java.lang.ref.Reference; +import java.util.List; + +/** + * LightsManager manages an input device's lights {@link android.hardware.input.Light}. + */ +class InputDeviceLightsManager extends LightsManager { + private static final String TAG = "InputDeviceLightsManager"; + private static final boolean DEBUG = false; + + private final InputManager mInputManager; + + // The input device ID. + private final int mDeviceId; + // Package name + private final String mPackageName; + + InputDeviceLightsManager(InputManager inputManager, int deviceId) { + super(ActivityThread.currentActivityThread().getSystemContext()); + mInputManager = inputManager; + mDeviceId = deviceId; + mPackageName = ActivityThread.currentPackageName(); + } + + /** + * Returns the lights available on the device. + * + * @return A list of available lights + */ + @Override + public @NonNull List<Light> getLights() { + return mInputManager.getLights(mDeviceId); + } + + /** + * Returns the state of a specified light. + * + * @hide + */ + @Override + public @NonNull LightState getLightState(@NonNull Light light) { + Preconditions.checkNotNull(light); + return mInputManager.getLightState(mDeviceId, light); + } + + /** + * Creates a new LightsSession that can be used to control the device lights. + */ + @Override + public @NonNull LightsSession openSession() { + final LightsSession session = new InputDeviceLightsSession(); + mInputManager.openLightSession(mDeviceId, mPackageName, session.getToken()); + return session; + } + + /** + * Encapsulates a session that can be used to control device lights and represents the lifetime + * of the requests. + */ + public final class InputDeviceLightsSession extends LightsManager.LightsSession + implements AutoCloseable { + + private final CloseGuard mCloseGuard = new CloseGuard(); + private boolean mClosed = false; + + /** + * Instantiated by {@link LightsManager#openSession()}. + */ + private InputDeviceLightsSession() { + mCloseGuard.open("close"); + } + + /** + * Sends a request to modify the states of multiple lights. + * + * @param request the settings for lights that should change + */ + @Override + public void requestLights(@NonNull LightsRequest request) { + Preconditions.checkNotNull(request); + Preconditions.checkArgument(!mClosed); + + mInputManager.requestLights(mDeviceId, request, getToken()); + } + + /** + * Closes the session, reverting all changes made through it. + */ + @Override + public void close() { + if (!mClosed) { + mInputManager.closeLightSession(mDeviceId, getToken()); + mClosed = true; + mCloseGuard.close(); + } + Reference.reachabilityFence(this); + } + + /** @hide */ + @Override + protected void finalize() throws Throwable { + try { + mCloseGuard.warnIfOpen(); + close(); + } finally { + super.finalize(); + } + } + } + +} diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index 8a01c660ebd0..e15d6298d63d 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -30,6 +30,10 @@ import android.compat.annotation.ChangeId; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.hardware.SensorManager; +import android.hardware.lights.Light; +import android.hardware.lights.LightState; +import android.hardware.lights.LightsManager; +import android.hardware.lights.LightsRequest; import android.os.BlockUntrustedTouchesMode; import android.os.Build; import android.os.CombinedVibrationEffect; @@ -1409,7 +1413,7 @@ public final class InputManager { } /** - * Gets a vibrator service associated with an input device, always create a new instance. + * Gets a vibrator service associated with an input device, always creates a new instance. * @return The vibrator, never null. * @hide */ @@ -1418,7 +1422,7 @@ public final class InputManager { } /** - * Gets a vibrator manager service associated with an input device, always create a new + * Gets a vibrator manager service associated with an input device, always creates a new * instance. * @return The vibrator manager, never null. * @hide @@ -1486,10 +1490,8 @@ public final class InputManager { /** * Register input device vibrator state listener - * - * @hide */ - public boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) { + boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) { try { return mIm.registerVibratorStateListener(deviceId, listener); } catch (RemoteException ex) { @@ -1499,10 +1501,8 @@ public final class InputManager { /** * Unregister input device vibrator state listener - * - * @hide */ - public boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) { + boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) { try { return mIm.unregisterVibratorStateListener(deviceId, listener); } catch (RemoteException ex) { @@ -1511,7 +1511,7 @@ public final class InputManager { } /** - * Gets a sensor manager service associated with an input device, always create a new instance. + * Gets a sensor manager service associated with an input device, always creates a new instance. * @return The sensor manager, never null. * @hide */ @@ -1533,6 +1533,86 @@ public final class InputManager { } /** + * Gets a lights manager associated with an input device, always creates a new instance. + * @return The lights manager, never null. + * @hide + */ + @NonNull + public LightsManager getInputDeviceLightsManager(int deviceId) { + return new InputDeviceLightsManager(InputManager.this, deviceId); + } + + /** + * Gets a list of light objects associated with an input device. + * @return The list of lights, never null. + */ + @NonNull List<Light> getLights(int deviceId) { + try { + return mIm.getLights(deviceId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns the state of an input device light. + * @return the light state + */ + @NonNull LightState getLightState(int deviceId, @NonNull Light light) { + try { + return mIm.getLightState(deviceId, light.getId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Request to modify the states of multiple lights. + * + * @param request the settings for lights that should change + */ + void requestLights(int deviceId, @NonNull LightsRequest request, IBinder token) { + try { + List<Integer> lightIdList = request.getLights(); + int[] lightIds = new int[lightIdList.size()]; + for (int i = 0; i < lightIds.length; i++) { + lightIds[i] = lightIdList.get(i); + } + List<LightState> lightStateList = request.getLightStates(); + mIm.setLightStates(deviceId, lightIds, + lightStateList.toArray(new LightState[lightStateList.size()]), + token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Open light session for input device manager + * + * @param token The token for the light session + */ + void openLightSession(int deviceId, String opPkg, @NonNull IBinder token) { + try { + mIm.openLightSession(deviceId, opPkg, token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Close light session + * + */ + void closeLightSession(int deviceId, @NonNull IBinder token) { + try { + mIm.closeLightSession(deviceId, token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Listens for changes in input devices. */ public interface InputDeviceListener { diff --git a/core/java/android/hardware/input/OWNERS b/core/java/android/hardware/input/OWNERS index 25e02e1aa6f3..c390b33fa174 100644 --- a/core/java/android/hardware/input/OWNERS +++ b/core/java/android/hardware/input/OWNERS @@ -1,6 +1,3 @@ # Bug component: 136048 include /services/core/java/com/android/server/input/OWNERS - -michaelwr@google.com -svv@google.com diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java index da270182052d..7bfff5d3af97 100644 --- a/core/java/android/hardware/lights/Light.java +++ b/core/java/android/hardware/lights/Light.java @@ -16,22 +16,56 @@ package android.hardware.lights; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Represents a logical light on the device. * - * @hide */ -@SystemApi public final class Light implements Parcelable { + // These enum values copy the values from {@link com.android.server.lights.LightsManager} + // and the light HAL. Since 0-7 are lights reserved for system use, 8 for microphone light is + // defined in {@link android.hardware.lights.LightsManager}, following types are available + // through this API. + /** Type for lights that indicate microphone usage */ + public static final int LIGHT_TYPE_MICROPHONE = 8; + + /** + * Type for lights that indicate a monochrome color LED light. + */ + public static final int LIGHT_TYPE_INPUT_SINGLE = 9; + + /** + * Type for lights that indicate a group of LED lights representing player ID. + */ + public static final int LIGHT_TYPE_INPUT_PLAYER_ID = 10; + + /** + * Type for lights that indicate a color LED light. + */ + public static final int LIGHT_TYPE_INPUT_RGB = 11; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"LIGHT_TYPE_"}, + value = { + LIGHT_TYPE_INPUT_PLAYER_ID, + LIGHT_TYPE_INPUT_SINGLE, + LIGHT_TYPE_INPUT_RGB, + }) + public @interface LightType {} + private final int mId; private final int mOrdinal; private final int mType; + private final String mName; /** * Creates a new light with the given data. @@ -39,15 +73,26 @@ public final class Light implements Parcelable { * @hide */ public Light(int id, int ordinal, int type) { + this(id, ordinal, type, "Light"); + } + + /** + * Creates a new light with the given data. + * + * @hide + */ + public Light(int id, int ordinal, int type, String name) { mId = id; mOrdinal = ordinal; mType = type; + mName = name; } private Light(@NonNull Parcel in) { mId = in.readInt(); mOrdinal = in.readInt(); mType = in.readInt(); + mName = in.readString(); } /** Implement the Parcelable interface */ @@ -56,6 +101,7 @@ public final class Light implements Parcelable { dest.writeInt(mId); dest.writeInt(mOrdinal); dest.writeInt(mType); + dest.writeString(mName); } /** Implement the Parcelable interface */ @@ -100,6 +146,14 @@ public final class Light implements Parcelable { } /** + * Returns the name of the light. + */ + @NonNull + public String getName() { + return mName; + } + + /** * Returns the ordinal of the light. * * <p>This is a sort key that represents the physical order of lights on the device with the diff --git a/core/java/android/hardware/lights/LightState.java b/core/java/android/hardware/lights/LightState.java index cd39e6df91a9..650b383eeb0f 100644 --- a/core/java/android/hardware/lights/LightState.java +++ b/core/java/android/hardware/lights/LightState.java @@ -32,36 +32,93 @@ import android.os.Parcelable; * will be converted to only a brightness value and that will be used for the light's single * channel. * - * @hide */ -@SystemApi public final class LightState implements Parcelable { private final int mColor; + private final int mPlayerId; /** - * Creates a new LightState with the desired color and intensity. + * Creates a new LightState with the desired color and intensity, for a light type + * of RBG color or monochrome color. * * @param color the desired color and intensity in ARGB format. + * @deprecated this has been replaced with {@link android.hardware.lights.LightState#forColor } + * @hide */ + @Deprecated + @SystemApi public LightState(@ColorInt int color) { + this(color, 0); + } + + /** + * Creates a new LightState with the desired color and intensity, and the player Id. + * Player Id will only be applied on Light type + * {@link android.hardware.lights.Light#LIGHT_TYPE_INPUT_PLAYER_ID} + * + * @param color the desired color and intensity in ARGB format. + * @hide + */ + public LightState(@ColorInt int color, int playerId) { mColor = color; + mPlayerId = playerId; + } + + /** + * Creates a new LightState with the desired color and intensity, for a light type + * of RBG color or single monochrome color. + * + * @param color the desired color and intensity in ARGB format. + * @return The LightState object contains the color. + */ + @NonNull + public static LightState forColor(@ColorInt int color) { + return new LightState(color, 0); + } + + /** + * Creates a new LightState with the desired player id, for a light of type + * {@link android.hardware.lights.Light#LIGHT_TYPE_INPUT_PLAYER_ID}. + * + * @param playerId the desired player id. + * @return The LightState object contains the player id. + */ + @NonNull + public static LightState forPlayerId(int playerId) { + return new LightState(0, playerId); } + /** + * Creates a new LightState from a parcel object. + */ private LightState(@NonNull Parcel in) { mColor = in.readInt(); + mPlayerId = in.readInt(); } /** - * Return the color and intensity associated with this LightState. - * @return the color and intensity in ARGB format. The A channel is ignored. + * Returns the color and intensity associated with this LightState. + * @return the color and intensity in ARGB format. The A channel is ignored. return 0 when + * calling LightsManager.getLightState with LIGHT_TYPE_INPUT_PLAYER_ID. */ public @ColorInt int getColor() { return mColor; } + /** + * Returns the player ID associated with this LightState for Light type + * {@link android.hardware.lights.Light#LIGHT_TYPE_INPUT_PLAYER_ID}, + * or 0 for other types. + * @return the player ID. + */ + public int getPlayerId() { + return mPlayerId; + } + @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mColor); + dest.writeInt(mPlayerId); } @Override @@ -69,6 +126,12 @@ public final class LightState implements Parcelable { return 0; } + @Override + public String toString() { + return "LightState{Color=0x" + Integer.toHexString(mColor) + ", PlayerId=" + + mPlayerId + "}"; + } + public static final @NonNull Parcelable.Creator<LightState> CREATOR = new Parcelable.Creator<LightState>() { public LightState createFromParcel(Parcel in) { diff --git a/core/java/android/hardware/lights/LightsManager.java b/core/java/android/hardware/lights/LightsManager.java index 33e5fcaf2abb..8fd56db33c4b 100644 --- a/core/java/android/hardware/lights/LightsManager.java +++ b/core/java/android/hardware/lights/LightsManager.java @@ -16,43 +16,38 @@ package android.hardware.lights; -import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.TestApi; import android.content.Context; import android.os.Binder; import android.os.IBinder; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.ServiceManager.ServiceNotFoundException; -import android.util.CloseGuard; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.lang.ref.Reference; import java.util.List; /** * The LightsManager class allows control over device lights. * - * @hide */ -@SystemApi @SystemService(Context.LIGHTS_SERVICE) -public final class LightsManager { +public abstract class LightsManager { private static final String TAG = "LightsManager"; + @NonNull private final Context mContext; // These enum values copy the values from {@link com.android.server.lights.LightsManager} // and the light HAL. Since 0-7 are lights reserved for system use, only the microphone light - // is available through this API. - /** Type for lights that indicate microphone usage */ + // and following types are available through this API. + /** Type for lights that indicate microphone usage + * @deprecated this has been moved to {@link android.hardware.lights.Light } + * @hide + */ + @Deprecated + @SystemApi public static final int LIGHT_TYPE_MICROPHONE = 8; /** @hide */ @@ -63,28 +58,11 @@ public final class LightsManager { }) public @interface LightType {} - @NonNull private final Context mContext; - @NonNull private final ILightsManager mService; - /** - * Creates a LightsManager. - * - * @hide + * @hide to prevent subclassing from outside of the framework */ - public LightsManager(@NonNull Context context) throws ServiceNotFoundException { - this(context, ILightsManager.Stub.asInterface( - ServiceManager.getServiceOrThrow(Context.LIGHTS_SERVICE))); - } - - /** - * Creates a LightsManager with a provided service implementation. - * - * @hide - */ - @VisibleForTesting - public LightsManager(@NonNull Context context, @NonNull ILightsManager service) { + public LightsManager(Context context) { mContext = Preconditions.checkNotNull(context); - mService = Preconditions.checkNotNull(service); } /** @@ -92,112 +70,44 @@ public final class LightsManager { * * @return A list of available lights */ - @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) - public @NonNull List<Light> getLights() { - try { - return mService.getLights(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } + public @NonNull abstract List<Light> getLights(); /** * Returns the state of a specified light. * - * @hide */ - @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) - @TestApi - public @NonNull LightState getLightState(@NonNull Light light) { - Preconditions.checkNotNull(light); - try { - return mService.getLightState(light.getId()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } + public abstract @NonNull LightState getLightState(@NonNull Light light); /** * Creates a new LightsSession that can be used to control the device lights. */ - @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) - public @NonNull LightsSession openSession() { - try { - final LightsSession session = new LightsSession(); - mService.openSession(session.mToken); - return session; - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } + public abstract @NonNull LightsSession openSession(); /** * Encapsulates a session that can be used to control device lights and represents the lifetime * of the requests. */ - public final class LightsSession implements AutoCloseable { - + public abstract static class LightsSession implements AutoCloseable { private final IBinder mToken = new Binder(); - - private final CloseGuard mCloseGuard = new CloseGuard(); - private boolean mClosed = false; - - /** - * Instantiated by {@link LightsManager#openSession()}. - */ - @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) - private LightsSession() { - mCloseGuard.open("close"); - } - /** * Sends a request to modify the states of multiple lights. * - * <p>This method only controls lights that aren't overridden by higher-priority sessions. - * Additionally, lights not controlled by this session can be controlled by lower-priority - * sessions. - * * @param request the settings for lights that should change */ - @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) - public void requestLights(@NonNull LightsRequest request) { - Preconditions.checkNotNull(request); - if (!mClosed) { - try { - mService.setLightStates(mToken, request.mLightIds, request.mLightStates); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - } + public abstract void requestLights(@NonNull LightsRequest request); - /** - * Closes the session, reverting all changes made through it. - */ - @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) @Override - public void close() { - if (!mClosed) { - try { - mService.closeSession(mToken); - mClosed = true; - mCloseGuard.close(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - Reference.reachabilityFence(this); - } + public abstract void close(); - /** @hide */ - @Override - protected void finalize() throws Throwable { - try { - mCloseGuard.warnIfOpen(); - close(); - } finally { - super.finalize(); - } + /** + * Get the token of a light session. + * + * @return Binder token of the light session. + * @hide + */ + public @NonNull IBinder getToken() { + return mToken; } } + } diff --git a/core/java/android/hardware/lights/LightsRequest.java b/core/java/android/hardware/lights/LightsRequest.java index a318992c35ee..2626a461aaf5 100644 --- a/core/java/android/hardware/lights/LightsRequest.java +++ b/core/java/android/hardware/lights/LightsRequest.java @@ -17,17 +17,17 @@ package android.hardware.lights; import android.annotation.NonNull; -import android.annotation.SystemApi; import android.util.SparseArray; import com.android.internal.util.Preconditions; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; /** * Encapsulates a request to modify the state of multiple lights. * - * @hide */ -@SystemApi public final class LightsRequest { /** Visible to {@link LightsManager.Session}. */ @@ -50,6 +50,30 @@ public final class LightsRequest { } /** + * Get a list of Light as ids. The ids will returned in same order as the lights passed + * in Builder. + * + * @return List of light ids + */ + public @NonNull List<Integer> getLights() { + List<Integer> lightList = new ArrayList<Integer>(mLightIds.length); + for (int i = 0; i < mLightIds.length; i++) { + lightList.add(mLightIds[i]); + } + return lightList; + } + + /** + * Get a list of LightState. The states will be returned in same order as the light states + * passed in Builder. + * + * @return List of light states + */ + public @NonNull List<LightState> getLightStates() { + return Arrays.asList(mLightStates); + } + + /** * Builder for creating device light change requests. */ public static final class Builder { @@ -62,7 +86,7 @@ public final class LightsRequest { * @param light the light to modify * @param state the desired color and intensity of the light */ - public @NonNull Builder setLight(@NonNull Light light, @NonNull LightState state) { + public @NonNull Builder addLight(@NonNull Light light, @NonNull LightState state) { Preconditions.checkNotNull(light); Preconditions.checkNotNull(state); mChanges.put(light.getId(), state); diff --git a/core/java/android/hardware/lights/SystemLightsManager.java b/core/java/android/hardware/lights/SystemLightsManager.java new file mode 100644 index 000000000000..726a61359c01 --- /dev/null +++ b/core/java/android/hardware/lights/SystemLightsManager.java @@ -0,0 +1,181 @@ +/* + * 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.hardware.lights; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.content.Context; +import android.hardware.lights.LightsManager.LightsSession; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.ServiceManager.ServiceNotFoundException; +import android.util.CloseGuard; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; + +import java.lang.ref.Reference; +import java.util.List; + +/** + * The LightsManager class allows control over device lights. + * + * @hide + */ +public final class SystemLightsManager extends LightsManager { + private static final String TAG = "LightsManager"; + + @NonNull private final ILightsManager mService; + + /** + * Creates a SystemLightsManager. + * + * @hide + */ + public SystemLightsManager(@NonNull Context context) throws ServiceNotFoundException { + this(context, ILightsManager.Stub.asInterface( + ServiceManager.getServiceOrThrow(Context.LIGHTS_SERVICE))); + } + + /** + * Creates a SystemLightsManager with a provided service implementation. + * + * @hide + */ + @VisibleForTesting + public SystemLightsManager(@NonNull Context context, @NonNull ILightsManager service) { + super(context); + mService = Preconditions.checkNotNull(service); + } + + /** + * Returns the lights available on the device. + * + * @return A list of available lights + */ + @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) + @Override + public @NonNull List<Light> getLights() { + try { + return mService.getLights(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns the state of a specified light. + * + * @hide + */ + @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) + @Override + public @NonNull LightState getLightState(@NonNull Light light) { + Preconditions.checkNotNull(light); + try { + return mService.getLightState(light.getId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Creates a new LightsSession that can be used to control the device lights. + */ + @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) + @Override + public @NonNull LightsSession openSession() { + try { + final LightsSession session = new SystemLightsSession(); + mService.openSession(session.getToken()); + return session; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Encapsulates a session that can be used to control device lights and represents the lifetime + * of the requests. + */ + public final class SystemLightsSession extends LightsManager.LightsSession + implements AutoCloseable { + + private final CloseGuard mCloseGuard = new CloseGuard(); + private boolean mClosed = false; + + /** + * Instantiated by {@link LightsManager#openSession()}. + */ + @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) + private SystemLightsSession() { + mCloseGuard.open("close"); + } + + /** + * Sends a request to modify the states of multiple lights. + * + * <p>This method only controls lights that aren't overridden by higher-priority sessions. + * Additionally, lights not controlled by this session can be controlled by lower-priority + * sessions. + * + * @param request the settings for lights that should change + */ + @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) + @Override + public void requestLights(@NonNull LightsRequest request) { + Preconditions.checkNotNull(request); + if (!mClosed) { + try { + mService.setLightStates(getToken(), request.mLightIds, request.mLightStates); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Closes the session, reverting all changes made through it. + */ + @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) + @Override + public void close() { + if (!mClosed) { + try { + mService.closeSession(getToken()); + mClosed = true; + mCloseGuard.close(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + Reference.reachabilityFence(this); + } + + /** @hide */ + @Override + protected void finalize() throws Throwable { + try { + mCloseGuard.warnIfOpen(); + close(); + } finally { + super.finalize(); + } + } + } +} diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index 5cfcd667632b..9198eb74d1f8 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -171,7 +171,7 @@ class IInputMethodWrapper extends IInputMethod.Stub SomeArgs args = (SomeArgs) msg.obj; try { inputMethod.initializeInternal((IBinder) args.arg1, msg.arg1, - (IInputMethodPrivilegedOperations) args.arg2); + (IInputMethodPrivilegedOperations) args.arg2, (int) args.arg3); } finally { args.recycle(); } @@ -280,9 +280,10 @@ class IInputMethodWrapper extends IInputMethod.Stub @BinderThread @Override public void initializeInternal(IBinder token, int displayId, - IInputMethodPrivilegedOperations privOps) { + IInputMethodPrivilegedOperations privOps, int configChanges) { mCaller.executeOrSendMessage( - mCaller.obtainMessageIOO(DO_INITIALIZE_INTERNAL, displayId, token, privOps)); + mCaller.obtainMessageIOOO(DO_INITIALIZE_INTERNAL, displayId, token, privOps, + configChanges)); } @BinderThread diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 7e2be01feb01..03dd3067e64e 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -70,6 +70,7 @@ import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; +import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -131,6 +132,7 @@ import android.widget.TextView; import android.window.WindowMetricsHelper; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.inputmethod.IInputContentUriToken; import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; import com.android.internal.inputmethod.InputMethodPrivilegedOperations; @@ -513,6 +515,8 @@ public class InputMethodService extends AbstractInputMethodService { private boolean mIsAutomotive; private Handler mHandler; private boolean mImeSurfaceScheduledForRemoval; + private Configuration mLastKnownConfig; + private int mHandledConfigChanges; /** * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#showSoftInput} @@ -588,12 +592,14 @@ public class InputMethodService extends AbstractInputMethodService { @MainThread @Override public final void initializeInternal(@NonNull IBinder token, int displayId, - IInputMethodPrivilegedOperations privilegedOperations) { + IInputMethodPrivilegedOperations privilegedOperations, + int configChanges) { if (InputMethodPrivilegedOperationsRegistry.isRegistered(token)) { Log.w(TAG, "The token has already registered, ignore this initialization."); return; } Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initializeInternal"); + mHandledConfigChanges = configChanges; mPrivOps.set(privilegedOperations); InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps); updateInputMethodDisplay(displayId); @@ -821,6 +827,9 @@ public class InputMethodService extends AbstractInputMethodService { setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition); } final boolean isVisible = isInputViewShown(); + if (isVisible && getResources() != null) { + mLastKnownConfig = getResources().getConfiguration(); + } final boolean visibilityChanged = isVisible != wasVisible; if (resultReceiver != null) { resultReceiver.send(visibilityChanged @@ -1428,10 +1437,37 @@ public class InputMethodService extends AbstractInputMethodService { * state: {@link #onStartInput} if input is active, and * {@link #onCreateInputView} and {@link #onStartInputView} and related * appropriate functions if the UI is displayed. + * <p>Starting with {@link Build.VERSION_CODES#S}, IMEs can opt into handling configuration + * changes themselves instead of being restarted with + * {@link android.R.styleable#InputMethod_configChanges}. */ @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - resetStateForNewConfiguration(); + if (shouldImeRestartForConfig(newConfig)) { + resetStateForNewConfiguration(); + } + } + + /** + * @return {@code true} if {@link InputMethodService} needs to restart to handle + * .{@link #onConfigurationChanged(Configuration)} + */ + @VisibleForTesting + boolean shouldImeRestartForConfig(@NonNull Configuration newConfig) { + if (mLastKnownConfig == null) { + return true; + } + // If the new config is the same as the config this Service is already running with, + // then don't bother calling resetStateForNewConfiguration. + int diff = mLastKnownConfig.diffPublicOnly(newConfig); + if (diff != 0) { + // remove attrs not-relevant to IME service. + diff &= ActivityInfo.CONFIG_KEYBOARD_HIDDEN; + diff &= ActivityInfo.CONFIG_KEYBOARD; + diff &= ActivityInfo.CONFIG_NAVIGATION; + } + int unhandledDiff = (diff & ~mHandledConfigChanges); + return unhandledDiff != 0; } private void resetStateForNewConfiguration() { @@ -3181,7 +3217,17 @@ public class InputMethodService extends AbstractInputMethodService { requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS); } } - + + @VisibleForTesting + void setLastKnownConfig(@NonNull Configuration config) { + mLastKnownConfig = config; + } + + @VisibleForTesting + void setHandledConfigChanges(int configChanges) { + mHandledConfigChanges = configChanges; + } + void startExtractingText(boolean inputChanged) { final ExtractEditText eet = mExtractEditText; if (eet != null && getCurrentInputStarted() diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 6353a25e745f..664120698971 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -16,14 +16,17 @@ package android.net; +import static android.app.ActivityManager.procStateToString; import static android.content.pm.PackageManager.GET_SIGNATURES; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.annotation.TestApi; import android.app.ActivityManager; +import android.app.ActivityManager.ProcessCapability; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; @@ -617,8 +620,18 @@ public class NetworkPolicyManager { * to access network when the device is idle or in battery saver mode. Otherwise, false. * @hide */ - public static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(int procState) { - return procState <= FOREGROUND_THRESHOLD_STATE; + public static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(@Nullable UidState uidState) { + if (uidState == null) { + return false; + } + return isProcStateAllowedWhileIdleOrPowerSaveMode(uidState.procState, uidState.capability); + } + + /** @hide */ + public static boolean isProcStateAllowedWhileIdleOrPowerSaveMode( + int procState, @ProcessCapability int capability) { + return procState <= FOREGROUND_THRESHOLD_STATE + || (capability & ActivityManager.PROCESS_CAPABILITY_NETWORK) != 0; } /** @@ -626,11 +639,44 @@ public class NetworkPolicyManager { * to access network when the device is in data saver mode. Otherwise, false. * @hide */ + public static boolean isProcStateAllowedWhileOnRestrictBackground(@Nullable UidState uidState) { + if (uidState == null) { + return false; + } + return isProcStateAllowedWhileOnRestrictBackground(uidState.procState); + } + + /** @hide */ public static boolean isProcStateAllowedWhileOnRestrictBackground(int procState) { + // Data saver and bg policy restrictions will only take procstate into account. return procState <= FOREGROUND_THRESHOLD_STATE; } /** @hide */ + public static final class UidState { + public int uid; + public int procState; + public int capability; + + public UidState(int uid, int procState, int capability) { + this.uid = uid; + this.procState = procState; + this.capability = capability; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("{procState="); + sb.append(procStateToString(procState)); + sb.append(",cap="); + ActivityManager.printCapabilitiesSummary(sb, capability); + sb.append("}"); + return sb.toString(); + } + } + + /** @hide */ @TestApi @NonNull public static String resolveNetworkId(@NonNull WifiConfiguration config) { diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java index 79f9e6ef2a97..dbb312720373 100644 --- a/core/java/android/net/NetworkStack.java +++ b/core/java/android/net/NetworkStack.java @@ -15,9 +15,6 @@ */ package android.net; -import static android.Manifest.permission.NETWORK_STACK; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; - import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -26,8 +23,7 @@ import android.content.Context; import android.os.IBinder; import android.os.ServiceManager; -import java.util.ArrayList; -import java.util.Arrays; +import com.android.net.module.util.PermissionUtils; /** * Constants and utilities for client code communicating with the network stack service. * @hide @@ -79,9 +75,14 @@ public class NetworkStack { * @param context {@link android.content.Context} for the process. * * @hide + * + * @deprecated Use {@link PermissionUtils#enforceNetworkStackPermission} instead. + * + * TODO: remove this method and let the users call to PermissionUtils directly. */ + @Deprecated public static void checkNetworkStackPermission(final @NonNull Context context) { - checkNetworkStackPermissionOr(context); + PermissionUtils.enforceNetworkStackPermission(context); } /** @@ -92,31 +93,14 @@ public class NetworkStack { * @param otherPermissions The set of permissions that could be the candidate permissions , or * empty string if none of other permissions needed. * @hide + * + * @deprecated Use {@link PermissionUtils#enforceNetworkStackPermissionOr} instead. + * + * TODO: remove this method and let the users call to PermissionUtils directly. */ + @Deprecated public static void checkNetworkStackPermissionOr(final @NonNull Context context, final @NonNull String... otherPermissions) { - ArrayList<String> permissions = new ArrayList<String>(Arrays.asList(otherPermissions)); - permissions.add(NETWORK_STACK); - permissions.add(PERMISSION_MAINLINE_NETWORK_STACK); - enforceAnyPermissionOf(context, permissions.toArray(new String[0])); + PermissionUtils.enforceNetworkStackPermissionOr(context, otherPermissions); } - - private static void enforceAnyPermissionOf(final @NonNull Context context, - final @NonNull String... permissions) { - if (!checkAnyPermissionOf(context, permissions)) { - throw new SecurityException("Requires one of the following permissions: " - + String.join(", ", permissions) + "."); - } - } - - private static boolean checkAnyPermissionOf(final @NonNull Context context, - final @NonNull String... permissions) { - for (String permission : permissions) { - if (context.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) { - return true; - } - } - return false; - } - } diff --git a/core/java/android/net/NetworkWatchlistManager.java b/core/java/android/net/NetworkWatchlistManager.java index 49047d3a0c87..8f6510ed3ea5 100644 --- a/core/java/android/net/NetworkWatchlistManager.java +++ b/core/java/android/net/NetworkWatchlistManager.java @@ -16,6 +16,8 @@ package android.net; +import android.annotation.Nullable; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; import android.os.RemoteException; @@ -29,6 +31,7 @@ import com.android.internal.util.Preconditions; * Class that manage network watchlist in system. * @hide */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @SystemService(Context.NETWORK_WATCHLIST_SERVICE) public class NetworkWatchlistManager { @@ -90,6 +93,7 @@ public class NetworkWatchlistManager { /** * Get Network Watchlist config file hash. */ + @Nullable public byte[] getWatchlistConfigHash() { try { return mNetworkWatchlistManager.getWatchlistConfigHash(); diff --git a/core/java/android/net/UidRange.java b/core/java/android/net/UidRange.java index b172ccc4e370..f0e7da78d669 100644 --- a/core/java/android/net/UidRange.java +++ b/core/java/android/net/UidRange.java @@ -42,10 +42,6 @@ public final class UidRange implements Parcelable { stop = stopUid; } - public static UidRange createForUser(int userId) { - return new UidRange(userId * PER_USER_RANGE, (userId + 1) * PER_USER_RANGE - 1); - } - /** Creates a UidRange for the specified user. */ public static UidRange createForUser(UserHandle user) { final UserHandle nextUser = UserHandle.of(user.getIdentifier() + 1); diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java index e43b0b6fa635..f90fbaf1e0fb 100644 --- a/core/java/android/net/VpnService.java +++ b/core/java/android/net/VpnService.java @@ -596,7 +596,8 @@ public class VpnService extends Service { } } } - mRoutes.add(new RouteInfo(new IpPrefix(address, prefixLength), null)); + mRoutes.add(new RouteInfo(new IpPrefix(address, prefixLength), null, null, + RouteInfo.RTN_UNICAST)); mConfig.updateAllowedFamilies(address); return this; } diff --git a/core/java/android/net/vcn/IVcnStatusCallback.aidl b/core/java/android/net/vcn/IVcnStatusCallback.aidl index 555e9b5883e8..d91cef592d10 100644 --- a/core/java/android/net/vcn/IVcnStatusCallback.aidl +++ b/core/java/android/net/vcn/IVcnStatusCallback.aidl @@ -17,8 +17,9 @@ package android.net.vcn; /** @hide */ -interface IVcnStatusCallback { +oneway interface IVcnStatusCallback { void onEnteredSafeMode(); + void onVcnStatusChanged(int statusCode); void onGatewayConnectionError( in int[] gatewayNetworkCapabilities, int errorCode, diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index aea0ea988f50..eb8c251fec78 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -21,6 +21,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; import android.net.LinkProperties; @@ -72,8 +73,7 @@ import java.util.concurrent.Executor; public class VcnManager { @NonNull private static final String TAG = VcnManager.class.getSimpleName(); - private static final Map< - VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder> + private static final Map<VcnNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder> REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>(); @NonNull private final Context mContext; @@ -93,13 +93,13 @@ public class VcnManager { } /** - * Get all currently registered VcnUnderlyingNetworkPolicyListeners for testing purposes. + * Get all currently registered VcnNetworkPolicyListeners for testing purposes. * * @hide */ @VisibleForTesting(visibility = Visibility.PRIVATE) @NonNull - public static Map<VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder> + public static Map<VcnNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder> getAllPolicyListeners() { return Collections.unmodifiableMap(REGISTERED_POLICY_LISTENERS); } @@ -161,45 +161,126 @@ public class VcnManager { } } - // TODO: make VcnUnderlyingNetworkPolicyListener @SystemApi + // TODO(b/180537630): remove all VcnUnderlyingNetworkPolicyListener refs once Telephony is using + // the new VcnNetworkPolicyListener API /** * VcnUnderlyingNetworkPolicyListener is the interface through which internal system components * can register to receive updates for VCN-underlying Network policies from the System Server. * * @hide */ - public interface VcnUnderlyingNetworkPolicyListener { + public interface VcnUnderlyingNetworkPolicyListener extends VcnNetworkPolicyListener {} + + /** + * Add a listener for VCN-underlying network policy updates. + * + * @param executor the Executor that will be used for invoking all calls to the specified + * Listener + * @param listener the VcnUnderlyingNetworkPolicyListener to be added + * @throws SecurityException if the caller does not have permission NETWORK_FACTORY + * @throws IllegalStateException if the specified VcnUnderlyingNetworkPolicyListener is already + * registered + * @hide + */ + @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) + public void addVcnUnderlyingNetworkPolicyListener( + @NonNull Executor executor, @NonNull VcnUnderlyingNetworkPolicyListener listener) { + addVcnNetworkPolicyListener(executor, listener); + } + + /** + * Remove the specified VcnUnderlyingNetworkPolicyListener from VcnManager. + * + * <p>If the specified listener is not currently registered, this is a no-op. + * + * @param listener the VcnUnderlyingNetworkPolicyListener that will be removed + * @hide + */ + public void removeVcnUnderlyingNetworkPolicyListener( + @NonNull VcnUnderlyingNetworkPolicyListener listener) { + removeVcnNetworkPolicyListener(listener); + } + + /** + * Queries the underlying network policy for a network with the given parameters. + * + * <p>Prior to a new NetworkAgent being registered, or upon notification that Carrier VCN policy + * may have changed via {@link VcnUnderlyingNetworkPolicyListener#onPolicyChanged()}, a Network + * Provider MUST poll for the updated Network policy based on that Network's capabilities and + * properties. + * + * @param networkCapabilities the NetworkCapabilities to be used in determining the Network + * policy for this Network. + * @param linkProperties the LinkProperties to be used in determining the Network policy for + * this Network. + * @throws SecurityException if the caller does not have permission NETWORK_FACTORY + * @return the VcnUnderlyingNetworkPolicy to be used for this Network. + * @hide + */ + @NonNull + @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) + public VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy( + @NonNull NetworkCapabilities networkCapabilities, + @NonNull LinkProperties linkProperties) { + requireNonNull(networkCapabilities, "networkCapabilities must not be null"); + requireNonNull(linkProperties, "linkProperties must not be null"); + + try { + return mService.getUnderlyingNetworkPolicy(networkCapabilities, linkProperties); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * VcnNetworkPolicyListener is the interface through which internal system components (e.g. + * Network Factories) can register to receive updates for VCN-underlying Network policies from + * the System Server. + * + * <p>Any Network Factory that brings up Networks capable of being VCN-underlying Networks + * should register a VcnNetworkPolicyListener. VcnManager will then use this listener to notify + * the registrant when VCN Network policies change. Upon receiving this signal, the listener + * must check {@link VcnManager} for the current Network policy result for each of its Networks + * via {@link #applyVcnNetworkPolicy(NetworkCapabilities, LinkProperties)}. + * + * @hide + */ + @SystemApi + public interface VcnNetworkPolicyListener { /** * Notifies the implementation that the VCN's underlying Network policy has changed. * - * <p>After receiving this callback, implementations MUST poll VcnManager for the updated - * VcnUnderlyingNetworkPolicy via VcnManager#getUnderlyingNetworkPolicy. + * <p>After receiving this callback, implementations should get the current {@link + * VcnNetworkPolicyResult} via {@link #applyVcnNetworkPolicy(NetworkCapabilities, + * LinkProperties)}. */ void onPolicyChanged(); } /** - * Add a listener for VCN-underlying network policy updates. + * Add a listener for VCN-underlying Network policy updates. + * + * <p>A {@link VcnNetworkPolicyListener} is eligible to begin receiving callbacks once it is + * registered. No callbacks are guaranteed upon registration. * * @param executor the Executor that will be used for invoking all calls to the specified * Listener - * @param listener the VcnUnderlyingNetworkPolicyListener to be added + * @param listener the VcnNetworkPolicyListener to be added * @throws SecurityException if the caller does not have permission NETWORK_FACTORY - * @throws IllegalArgumentException if the specified VcnUnderlyingNetworkPolicyListener is - * already registered + * @throws IllegalStateException if the specified VcnNetworkPolicyListener is already registered * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) - public void addVcnUnderlyingNetworkPolicyListener( - @NonNull Executor executor, @NonNull VcnUnderlyingNetworkPolicyListener listener) { + public void addVcnNetworkPolicyListener( + @NonNull Executor executor, @NonNull VcnNetworkPolicyListener listener) { requireNonNull(executor, "executor must not be null"); requireNonNull(listener, "listener must not be null"); VcnUnderlyingNetworkPolicyListenerBinder binder = new VcnUnderlyingNetworkPolicyListenerBinder(executor, listener); if (REGISTERED_POLICY_LISTENERS.putIfAbsent(listener, binder) != null) { - throw new IllegalArgumentException( - "Attempting to add a listener that is already in use"); + throw new IllegalStateException("listener is already registered with VcnManager"); } try { @@ -211,15 +292,15 @@ public class VcnManager { } /** - * Remove the specified VcnUnderlyingNetworkPolicyListener from VcnManager. + * Remove the specified VcnNetworkPolicyListener from VcnManager. * * <p>If the specified listener is not currently registered, this is a no-op. * - * @param listener the VcnUnderlyingNetworkPolicyListener that will be removed + * @param listener the VcnNetworkPolicyListener that will be removed * @hide */ - public void removeVcnUnderlyingNetworkPolicyListener( - @NonNull VcnUnderlyingNetworkPolicyListener listener) { + @SystemApi + public void removeVcnNetworkPolicyListener(@NonNull VcnNetworkPolicyListener listener) { requireNonNull(listener, "listener must not be null"); VcnUnderlyingNetworkPolicyListenerBinder binder = @@ -236,39 +317,88 @@ public class VcnManager { } /** - * Queries the underlying network policy for a network with the given parameters. + * Applies the network policy for a {@link android.net.Network} with the given parameters. * * <p>Prior to a new NetworkAgent being registered, or upon notification that Carrier VCN policy - * may have changed via {@link VcnUnderlyingNetworkPolicyListener#onPolicyChanged()}, a Network - * Provider MUST poll for the updated Network policy based on that Network's capabilities and - * properties. + * may have changed via {@link VcnNetworkPolicyListener#onPolicyChanged()}, a Network Provider + * MUST poll for the updated Network policy based on that Network's capabilities and properties. * * @param networkCapabilities the NetworkCapabilities to be used in determining the Network - * policy for this Network. - * @param linkProperties the LinkProperties to be used in determining the Network policy for - * this Network. + * policy result for this Network. + * @param linkProperties the LinkProperties to be used in determining the Network policy result + * for this Network. * @throws SecurityException if the caller does not have permission NETWORK_FACTORY - * @return the VcnUnderlyingNetworkPolicy to be used for this Network. + * @return the {@link VcnNetworkPolicyResult} to be used for this Network. * @hide */ @NonNull + @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) - public VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy( + public VcnNetworkPolicyResult applyVcnNetworkPolicy( @NonNull NetworkCapabilities networkCapabilities, @NonNull LinkProperties linkProperties) { requireNonNull(networkCapabilities, "networkCapabilities must not be null"); requireNonNull(linkProperties, "linkProperties must not be null"); - try { - return mService.getUnderlyingNetworkPolicy(networkCapabilities, linkProperties); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + final VcnUnderlyingNetworkPolicy policy = + getUnderlyingNetworkPolicy(networkCapabilities, linkProperties); + return new VcnNetworkPolicyResult( + policy.isTeardownRequested(), policy.getMergedNetworkCapabilities()); } /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef({ + VCN_STATUS_CODE_NOT_CONFIGURED, + VCN_STATUS_CODE_INACTIVE, + VCN_STATUS_CODE_ACTIVE, + VCN_STATUS_CODE_SAFE_MODE + }) + public @interface VcnStatusCode {} + + /** + * Value indicating that the VCN for the subscription group is not configured, or that the + * callback is not privileged for the subscription group. + * + * @hide + */ + public static final int VCN_STATUS_CODE_NOT_CONFIGURED = 0; + + /** + * Value indicating that the VCN for the subscription group is inactive. + * + * <p>A VCN is inactive if a {@link VcnConfig} is present for the subscription group, but the + * provisioning package is not privileged. + * + * @hide + */ + public static final int VCN_STATUS_CODE_INACTIVE = 1; + + /** + * Value indicating that the VCN for the subscription group is active. + * + * <p>A VCN is active if a {@link VcnConfig} is present for the subscription, the provisioning + * package is privileged, and the VCN is not in Safe Mode. In other words, a VCN is considered + * active while it is connecting, fully connected, and disconnecting. + * + * @hide + */ + public static final int VCN_STATUS_CODE_ACTIVE = 2; + + /** + * Value indicating that the VCN for the subscription group is in Safe Mode. + * + * <p>A VCN will be put into Safe Mode if any of the gateway connections were unable to + * establish a connection within a system-determined timeout (while underlying networks were + * available). + * + * @hide + */ + public static final int VCN_STATUS_CODE_SAFE_MODE = 3; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ VCN_ERROR_CODE_INTERNAL_ERROR, VCN_ERROR_CODE_CONFIG_ERROR, VCN_ERROR_CODE_NETWORK_ERROR @@ -323,8 +453,18 @@ public class VcnManager { * * <p>A VCN-configuring app may opt to exit safe mode by (re)setting the VCN configuration * via {@link #setVcnConfig(ParcelUuid, VcnConfig)}. + * + * @hide */ - public abstract void onEnteredSafeMode(); + public void onEnteredSafeMode() {} + + /** + * Invoked when status of the VCN for this callback's subscription group changes. + * + * @param statusCode the code for the status change encountered by this {@link + * VcnStatusCallback}'s subscription group. + */ + public abstract void onVcnStatusChanged(@VcnStatusCode int statusCode); /** * Invoked when a VCN Gateway Connection corresponding to this callback's subscription @@ -356,6 +496,11 @@ public class VcnManager { * <p>A {@link VcnStatusCallback} will only be invoked if the registering package has carrier * privileges for the specified subscription at the time of invocation. * + * <p>{@link VcnStatusCallback#onVcnStatusChanged(int)} will be invoked on registration with the + * current status for the specified subscription group's VCN. If the registrant is not + * privileged for this subscription group, {@link #VCN_STATUS_CODE_NOT_CONFIGURED} will be + * returned. + * * @param subscriptionGroup The subscription group to match for callbacks * @param executor The {@link Executor} to be used for invoking callbacks * @param callback The VcnStatusCallback to be registered @@ -415,18 +560,17 @@ public class VcnManager { } /** - * Binder wrapper for added VcnUnderlyingNetworkPolicyListeners to receive signals from System - * Server. + * Binder wrapper for added VcnNetworkPolicyListeners to receive signals from System Server. * * @hide */ private static class VcnUnderlyingNetworkPolicyListenerBinder extends IVcnUnderlyingNetworkPolicyListener.Stub { @NonNull private final Executor mExecutor; - @NonNull private final VcnUnderlyingNetworkPolicyListener mListener; + @NonNull private final VcnNetworkPolicyListener mListener; private VcnUnderlyingNetworkPolicyListenerBinder( - Executor executor, VcnUnderlyingNetworkPolicyListener listener) { + Executor executor, VcnNetworkPolicyListener listener) { mExecutor = executor; mListener = listener; } @@ -460,6 +604,12 @@ public class VcnManager { () -> mExecutor.execute(() -> mCallback.onEnteredSafeMode())); } + @Override + public void onVcnStatusChanged(@VcnStatusCode int statusCode) { + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> mCallback.onVcnStatusChanged(statusCode))); + } + // TODO(b/180521637): use ServiceSpecificException for safer Exception 'parceling' @Override public void onGatewayConnectionError( diff --git a/core/java/android/net/vcn/VcnNetworkPolicyResult.aidl b/core/java/android/net/vcn/VcnNetworkPolicyResult.aidl new file mode 100644 index 000000000000..3f13abe869da --- /dev/null +++ b/core/java/android/net/vcn/VcnNetworkPolicyResult.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.vcn; + +/** @hide */ +parcelable VcnNetworkPolicyResult; diff --git a/core/java/android/net/vcn/VcnNetworkPolicyResult.java b/core/java/android/net/vcn/VcnNetworkPolicyResult.java new file mode 100644 index 000000000000..5e938200639c --- /dev/null +++ b/core/java/android/net/vcn/VcnNetworkPolicyResult.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.vcn; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.net.NetworkCapabilities; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * VcnNetworkPolicyResult represents the Network policy result for a Network transport applying its + * VCN policy via {@link VcnManager#applyVcnNetworkPolicy(NetworkCapabilities, LinkProperties)}. + * + * <p>Bearers that are bringing up networks capable of acting as a VCN's underlying network should + * query for Network policy results upon any capability changes (e.g. changing of TRUSTED bit), and + * when prompted by VcnManagementService via {@link VcnManager.VcnNetworkPolicyListener}. + * + * @hide + */ +@SystemApi +public final class VcnNetworkPolicyResult implements Parcelable { + private final boolean mIsTearDownRequested; + private final NetworkCapabilities mNetworkCapabilities; + + /** + * Constructs a VcnNetworkPolicyResult with the specified parameters. + * + * @hide + */ + public VcnNetworkPolicyResult( + boolean isTearDownRequested, @NonNull NetworkCapabilities networkCapabilities) { + Objects.requireNonNull(networkCapabilities, "networkCapabilities must be non-null"); + + mIsTearDownRequested = isTearDownRequested; + mNetworkCapabilities = networkCapabilities; + } + + /** + * Returns whether this VCN policy result requires that the underlying Network should be torn + * down. + * + * <p>Upon querying for the current Network policy result, the bearer must check this method, + * and MUST tear down the corresponding Network if it returns true. + */ + public boolean isTeardownRequested() { + return mIsTearDownRequested; + } + + /** + * Returns the NetworkCapabilities that the bearer should be using for the corresponding + * Network. + */ + @NonNull + public NetworkCapabilities getNetworkCapabilities() { + return mNetworkCapabilities; + } + + @Override + public int hashCode() { + return Objects.hash(mIsTearDownRequested, mNetworkCapabilities); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof VcnNetworkPolicyResult)) return false; + final VcnNetworkPolicyResult that = (VcnNetworkPolicyResult) o; + + return mIsTearDownRequested == that.mIsTearDownRequested + && mNetworkCapabilities.equals(that.mNetworkCapabilities); + } + + /** {@inheritDoc} */ + @Override + public int describeContents() { + return 0; + } + + /** {@inheritDoc} */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeBoolean(mIsTearDownRequested); + dest.writeParcelable(mNetworkCapabilities, flags); + } + + /** Implement the Parcelable interface */ + public static final @NonNull Creator<VcnNetworkPolicyResult> CREATOR = + new Creator<VcnNetworkPolicyResult>() { + public VcnNetworkPolicyResult createFromParcel(Parcel in) { + return new VcnNetworkPolicyResult(in.readBoolean(), in.readParcelable(null)); + } + + public VcnNetworkPolicyResult[] newArray(int size) { + return new VcnNetworkPolicyResult[size]; + } + }; +} diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java index dd7c86d87ff2..b47d5642419e 100644 --- a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java +++ b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java @@ -33,8 +33,7 @@ import java.util.Objects; * @hide */ public final class VcnUnderlyingNetworkPolicy implements Parcelable { - private final boolean mIsTearDownRequested; - private final NetworkCapabilities mMergedNetworkCapabilities; + private final VcnNetworkPolicyResult mVcnNetworkPolicyResult; /** * Constructs a VcnUnderlyingNetworkPolicy with the specified parameters. @@ -46,8 +45,13 @@ public final class VcnUnderlyingNetworkPolicy implements Parcelable { Objects.requireNonNull( mergedNetworkCapabilities, "mergedNetworkCapabilities must be nonnull"); - mIsTearDownRequested = isTearDownRequested; - mMergedNetworkCapabilities = mergedNetworkCapabilities; + mVcnNetworkPolicyResult = + new VcnNetworkPolicyResult(isTearDownRequested, mergedNetworkCapabilities); + } + + private VcnUnderlyingNetworkPolicy(@NonNull VcnNetworkPolicyResult vcnNetworkPolicyResult) { + this.mVcnNetworkPolicyResult = + Objects.requireNonNull(vcnNetworkPolicyResult, "vcnNetworkPolicyResult"); } /** @@ -55,7 +59,7 @@ public final class VcnUnderlyingNetworkPolicy implements Parcelable { * be torn down. */ public boolean isTeardownRequested() { - return mIsTearDownRequested; + return mVcnNetworkPolicyResult.isTeardownRequested(); } /** @@ -64,12 +68,12 @@ public final class VcnUnderlyingNetworkPolicy implements Parcelable { */ @NonNull public NetworkCapabilities getMergedNetworkCapabilities() { - return mMergedNetworkCapabilities; + return mVcnNetworkPolicyResult.getNetworkCapabilities(); } @Override public int hashCode() { - return Objects.hash(mIsTearDownRequested, mMergedNetworkCapabilities); + return Objects.hash(mVcnNetworkPolicyResult); } @Override @@ -78,8 +82,7 @@ public final class VcnUnderlyingNetworkPolicy implements Parcelable { if (!(o instanceof VcnUnderlyingNetworkPolicy)) return false; final VcnUnderlyingNetworkPolicy that = (VcnUnderlyingNetworkPolicy) o; - return mIsTearDownRequested == that.mIsTearDownRequested - && mMergedNetworkCapabilities.equals(that.mMergedNetworkCapabilities); + return mVcnNetworkPolicyResult.equals(that.mVcnNetworkPolicyResult); } /** {@inheritDoc} */ @@ -91,16 +94,14 @@ public final class VcnUnderlyingNetworkPolicy implements Parcelable { /** {@inheritDoc} */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeBoolean(mIsTearDownRequested); - dest.writeParcelable(mMergedNetworkCapabilities, flags); + dest.writeParcelable(mVcnNetworkPolicyResult, flags); } /** Implement the Parcelable interface */ public static final @NonNull Creator<VcnUnderlyingNetworkPolicy> CREATOR = new Creator<VcnUnderlyingNetworkPolicy>() { public VcnUnderlyingNetworkPolicy createFromParcel(Parcel in) { - return new VcnUnderlyingNetworkPolicy( - in.readBoolean(), in.readParcelable(null)); + return new VcnUnderlyingNetworkPolicy(in.readParcelable(null)); } public VcnUnderlyingNetworkPolicy[] newArray(int size) { diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java index 305c686f8657..a435ac12d33c 100644 --- a/core/java/android/os/BugreportManager.java +++ b/core/java/android/os/BugreportManager.java @@ -25,6 +25,7 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressAutoDoc; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.annotation.WorkerThread; import android.app.ActivityManager; import android.content.Context; import android.util.Log; @@ -41,7 +42,15 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.concurrent.Executor; -/** Class that provides a privileged API to capture and consume bugreports. */ +/** + * Class that provides a privileged API to capture and consume bugreports. + * + * <p>This class may only be used by apps that currently have carrier privileges (see {@link + * android.telephony.TelephonyManager#hasCarrierPrivileges}) on an active SIM or priv-apps + * explicitly allowed by the device manufacturer. + * + * <p>Only one bugreport can be generated by the system at a time. + */ @SystemService(Context.BUGREPORT_SERVICE) public final class BugreportManager { @@ -56,7 +65,12 @@ public final class BugreportManager { mBinder = binder; } - /** An interface describing the callback for bugreport progress and status. */ + /** + * An interface describing the callback for bugreport progress and status. + * + * <p>In general, callers can expect to receive {@link #onProgress} calls as the bugreport + * progresses, followed by a terminal call to either {@link #onFinished} or {@link #onError}. + */ public abstract static class BugreportCallback { /** * Possible error codes taking a bugreport can encounter. @@ -75,15 +89,18 @@ public final class BugreportManager { }) public @interface BugreportErrorCode {} - /** The input options were invalid */ + /** + * The input options were invalid. For example, the destination file the app provided could + * not be written by the system. + */ public static final int BUGREPORT_ERROR_INVALID_INPUT = IDumpstateListener.BUGREPORT_ERROR_INVALID_INPUT; - /** A runtime error occurred */ + /** A runtime error occurred. */ public static final int BUGREPORT_ERROR_RUNTIME = IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR; - /** User denied consent to share the bugreport */ + /** User denied consent to share the bugreport. */ public static final int BUGREPORT_ERROR_USER_DENIED_CONSENT = IDumpstateListener.BUGREPORT_ERROR_USER_DENIED_CONSENT; @@ -149,6 +166,7 @@ public final class BugreportManager { */ @SystemApi @RequiresPermission(android.Manifest.permission.DUMP) + @WorkerThread public void startBugreport( @NonNull ParcelFileDescriptor bugreportFd, @Nullable ParcelFileDescriptor screenshotFd, @@ -222,6 +240,7 @@ public final class BugreportManager { * @param callback callback for progress and status updates. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @WorkerThread public void startConnectivityBugreport( @NonNull ParcelFileDescriptor bugreportFd, @NonNull @CallbackExecutor Executor executor, @@ -247,6 +266,7 @@ public final class BugreportManager { * @throws SecurityException if trying to cancel another app's bugreport in progress */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @WorkerThread public void cancelBugreport() { try { mBinder.cancelBugreport(-1 /* callingUid */, mContext.getOpPackageName()); diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index 93c1690e3813..43184ea4b9a9 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -26,6 +26,7 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.app.KeyguardManager; import android.app.PendingIntent; import android.compat.annotation.UnsupportedAppUsage; import android.content.BroadcastReceiver; @@ -631,10 +632,15 @@ public class RecoverySystem { /** * Prepare to apply an unattended update by asking the user for their Lock Screen Knowledge * Factor (LSKF). If supplied, the {@code intentSender} will be called when the system is setup - * and ready to apply the OTA. This API is expected to handle requests from multiple clients - * simultaneously, e.g. from ota and mainline. + * and ready to apply the OTA. <p> * - * <p> The behavior of multi-client Resume on Reboot works as follows + * <p> If the device doesn't setup a lock screen, i.e. by checking + * {@link KeyguardManager#isKeyguardSecure()}, this API call will fail and throw an exception. + * Callers are expected to use {@link PowerManager#reboot(String)} directly without going + * through the RoR flow. <p> + * + * <p> This API is expected to handle requests from multiple clients simultaneously, e.g. + * from ota and mainline. The behavior of multi-client Resume on Reboot works as follows * <li> Each client should call this function to prepare for Resume on Reboot before calling * {@link #rebootAndApply(Context, String, boolean)} </li> * <li> One client cannot clear the Resume on Reboot preparation of another client. </li> @@ -658,6 +664,13 @@ public class RecoverySystem { if (updateToken == null) { throw new NullPointerException("updateToken == null"); } + + KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class); + if (keyguardManager == null || !keyguardManager.isDeviceSecure()) { + throw new IOException("Failed to request LSKF because the device doesn't have a" + + " lock screen. "); + } + RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); if (!rs.requestLskf(context.getPackageName(), intentSender)) { throw new IOException("preparation for update failed"); diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index d6fa733927fb..b003d238c268 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -186,7 +186,8 @@ public abstract class Vibrator { /** * Return the ID of this vibrator. * - * @return The id of the vibrator controlled by this service. + * @return A non-negative integer representing the id of the vibrator controlled by this + * service, or -1 this service is not attached to any physical vibrator. */ public int getId() { return -1; diff --git a/core/java/android/os/VibratorManager.java b/core/java/android/os/VibratorManager.java index 5dd38b6cbd86..5a01814508e1 100644 --- a/core/java/android/os/VibratorManager.java +++ b/core/java/android/os/VibratorManager.java @@ -91,10 +91,10 @@ public abstract class VibratorManager { * * <p> * Pass in a {@link CombinedVibrationEffect} representing a combination of {@link - * VibrationEffect} to be played on one or more vibrators. + * VibrationEffect VibrationEffects} to be played on one or more vibrators. * </p> * - * @param effect an array of longs of times for which to turn the vibrator on or off. + * @param effect a combination of effects to be performed by one or more vibrators. */ @RequiresPermission(android.Manifest.permission.VIBRATE) public final void vibrate(@NonNull CombinedVibrationEffect effect) { @@ -109,7 +109,7 @@ public abstract class VibratorManager { * VibrationEffect} to be played on one or more vibrators. * </p> * - * @param effect an array of longs of times for which to turn the vibrator on or off. + * @param effect a combination of effects to be performed by one or more vibrators. * @param attributes {@link VibrationAttributes} corresponding to the vibration. For example, * specify {@link VibrationAttributes#USAGE_ALARM} for alarm vibrations or * {@link VibrationAttributes#USAGE_RINGTONE} for vibrations associated with diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java index a078e0434867..73520e07d118 100644 --- a/core/java/android/os/incremental/IncrementalFileStorages.java +++ b/core/java/android/os/incremental/IncrementalFileStorages.java @@ -59,7 +59,6 @@ public final class IncrementalFileStorages { /** * Set up files and directories used in an installation session. Only used by Incremental. * All the files will be created in defaultStorage. - * TODO(b/133435829): code clean up * * @throws IllegalStateException the session is not an Incremental installation session. * @throws IOException if fails to setup files or directories. @@ -73,12 +72,10 @@ public final class IncrementalFileStorages { @Nullable IStorageHealthListener healthListener, @NonNull List<InstallationFileParcel> addedFiles, @NonNull PerUidReadTimeouts[] perUidReadTimeouts, - IPackageLoadingProgressCallback progressCallback) throws IOException { - // TODO(b/136132412): validity check if session should not be incremental + @Nullable IPackageLoadingProgressCallback progressCallback) throws IOException { IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService( Context.INCREMENTAL_SERVICE); if (incrementalManager == null) { - // TODO(b/146080380): add incremental-specific error code throw new IOException("Failed to obtain incrementalManager."); } @@ -89,7 +86,6 @@ public final class IncrementalFileStorages { try { result.addApkFile(file); } catch (IOException e) { - // TODO(b/146080380): add incremental-specific error code throw new IOException( "Failed to add file to IncFS: " + file.name + ", reason: ", e); } @@ -203,7 +199,6 @@ public final class IncrementalFileStorages { /** * Resets the states and unbinds storage instances for an installation session. - * TODO(b/136132412): make sure unnecessary binds are removed but useful storages are kept */ public void cleanUp() { if (mDefaultStorage == null) { @@ -211,8 +206,8 @@ public final class IncrementalFileStorages { } try { + mIncrementalManager.unregisterLoadingProgressCallbacks(mStageDir.getAbsolutePath()); mDefaultStorage.unBind(mStageDir.getAbsolutePath()); - mDefaultStorage.unregisterLoadingProgressListener(); } catch (IOException ignored) { } mDefaultStorage = null; diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java index 05899947c3df..592e98abae63 100644 --- a/core/java/android/os/incremental/IncrementalManager.java +++ b/core/java/android/os/incremental/IncrementalManager.java @@ -241,10 +241,12 @@ public final class IncrementalManager { } /** - * Checks if device supports V2 calls (e.g. PerUid). + * 0 - IncFs is disabled. + * 1 - IncFs v1, core features, no PerUid support. Optional in R. + * 2 - IncFs v2, PerUid support, fs-verity support. Required in S. */ - public static boolean isV2Available() { - return nativeIsV2Available(); + public static int getVersion() { + return nativeIsEnabled() ? nativeIsV2Available() ? 2 : 1 : 0; } /** @@ -342,7 +344,6 @@ public final class IncrementalManager { storage.unregisterLoadingProgressListener(); } - // TODO(b/165841827): handle reboot and app update public boolean registerCallback(@NonNull IncrementalStorage storage, @NonNull IPackageLoadingProgressCallback callback) { final int storageId = storage.getId(); @@ -364,30 +365,6 @@ public final class IncrementalManager { return storage.registerLoadingProgressListener(this); } - public boolean unregisterCallback(@NonNull IncrementalStorage storage, - @NonNull IPackageLoadingProgressCallback callback) { - final int storageId = storage.getId(); - final RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage; - synchronized (mCallbacks) { - callbacksForStorage = mCallbacks.get(storageId); - if (callbacksForStorage == null) { - // no callback has ever been registered on this storage - return false; - } - if (!callbacksForStorage.unregister(callback)) { - // the callback was not registered - return false; - } - if (callbacksForStorage.getRegisteredCallbackCount() > 0) { - // other callbacks are still listening on this storage - return true; - } - mCallbacks.delete(storageId); - } - // stop listening for this storage - return storage.unregisterLoadingProgressListener(); - } - @Override public void onStorageLoadingProgressChanged(int storageId, float progress) { final RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage; diff --git a/core/java/android/os/strictmode/IncorrectContextUseViolation.java b/core/java/android/os/strictmode/IncorrectContextUseViolation.java index 11d26cab14b3..d8c22fd94ce9 100644 --- a/core/java/android/os/strictmode/IncorrectContextUseViolation.java +++ b/core/java/android/os/strictmode/IncorrectContextUseViolation.java @@ -24,7 +24,7 @@ import android.content.Context; * instance. * * @see Context#getSystemService(String) - * @see Context#isUiContext(Context) + * @see Context#isUiContext * @see android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse() */ public final class IncorrectContextUseViolation extends Violation { diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index e134c29520b2..4354920c83ec 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -275,6 +275,14 @@ public final class DeviceConfig { public static final String NAMESPACE_PROFCOLLECT_NATIVE_BOOT = "profcollect_native_boot"; /** + * Namespace for features related to Reboot Readiness detection. + * + * @hide + */ + @SystemApi + public static final String NAMESPACE_REBOOT_READINESS = "reboot_readiness"; + + /** * Namespace for Rollback flags that are applied immediately. * * @hide diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 6ee7eb25eb2f..345cc84e8593 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -417,6 +417,22 @@ public final class Settings { "android.settings.MANAGE_UNKNOWN_APP_SOURCES"; /** + * Activity Action: Show settings to allow configuration of + * {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission + * + * Input: Optionally, the Intent's data URI can specify the application package name to + * directly invoke the management GUI specific to the package name. For example + * "package:com.my.app". + * <p> + * Output: When a package data uri is passed as input, the activity result is set to + * {@link android.app.Activity#RESULT_OK} if the permission was granted to the app. Otherwise, + * the result is set to {@link android.app.Activity#RESULT_CANCELED}. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REQUEST_SCHEDULE_EXACT_ALARM = + "android.settings.REQUEST_SCHEDULE_EXACT_ALARM"; + + /** * Activity Action: Show settings to allow configuration of cross-profile access for apps * * Input: Optionally, the Intent's data URI can specify the application package name to @@ -10697,14 +10713,6 @@ public final class Settings { "hdmi_cec_switch_enabled"; /** - * HDMI CEC version to use. Defaults to v1.4b. - * @hide - */ - @Readable - public static final String HDMI_CEC_VERSION = - "hdmi_cec_version"; - - /** * Whether TV will automatically turn on upon reception of the CEC command * <Text View On> or <Image View On>. (0 = false, 1 = true) * @@ -14614,29 +14622,6 @@ public final class Settings { 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 - */ - @Readable - 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/provider/SimPhonebookContract.java b/core/java/android/provider/SimPhonebookContract.java index 074d5f167ec3..030b86339822 100644 --- a/core/java/android/provider/SimPhonebookContract.java +++ b/core/java/android/provider/SimPhonebookContract.java @@ -44,8 +44,11 @@ import java.util.Objects; * The contract between the provider of contact records on the device's SIM cards and applications. * Contains definitions of the supported URIs and columns. * - * <p>This content provider does not support any of the QUERY_ARG_SQL* bundle arguments. An - * IllegalArgumentException will be thrown if these are included. + * <h3>Permissions</h3> + * <p> + * Querying this provider requires {@link android.Manifest.permission#READ_CONTACTS} and writing + * to this provider requires {@link android.Manifest.permission#WRITE_CONTACTS} + * </p> */ public final class SimPhonebookContract { @@ -85,7 +88,73 @@ public final class SimPhonebookContract { } } - /** Constants for the contact records on a SIM card. */ + /** + * Constants for the contact records on a SIM card. + * + * <h3 id="simrecords-data">Data</h3> + * <p> + * Data is stored in a specific elementary file on a specific SIM card and these are isolated + * from each other. SIM cards are identified by their subscription ID. SIM cards may not support + * all or even any of the elementary file types. A SIM will have constraints on + * the values of the data that can be stored in each elementary file. The available SIMs, + * their supported elementary file types and the constraints on the data can be discovered by + * querying {@link ElementaryFiles#CONTENT_URI}. Each elementary file has a fixed capacity + * for the number of records that may be stored. This can be determined from the value + * of the {@link ElementaryFiles#MAX_RECORDS} column. + * </p> + * <p> + * The {@link SimRecords#PHONE_NUMBER} column can only contain dialable characters and this + * applies regardless of the SIM that is being used. See + * {@link android.telephony.PhoneNumberUtils#isDialable(char)} for more details. Additionally + * the phone number can contain at most {@link ElementaryFiles#PHONE_NUMBER_MAX_LENGTH} + * characters. The {@link SimRecords#NAME} column can contain at most + * {@link ElementaryFiles#NAME_MAX_LENGTH} bytes when it is encoded for storage on the SIM. + * Encoding is done internally and so the name should be provided unencoded but the number of + * bytes required to encode it will vary depending on the characters it contains. This length + * can be determined by calling + * {@link SimRecords#getEncodedNameLength(ContentResolver, String)}. + * </p> + * <h3>Operations </h3> + * <dl> + * <dd><b>Insert</b></dd> + * <p> + * Only {@link ElementaryFiles#EF_ADN} supports inserts. {@link SimRecords#PHONE_NUMBER} + * is a required column. If the value provided for this column is missing, null, empty + * or violates the requirements discussed in the <a href="#simrecords-data">Data</a> + * section above an {@link IllegalArgumentException} will be thrown. The + * {@link SimRecords#NAME} column may be omitted but if provided and it violates any of + * the requirements discussed in the <a href="#simrecords-data">Data</a> section above + * an {@link IllegalArgumentException} will be thrown. + * </p> + * <p> + * If an insert is not possible because the elementary file is full then an + * {@link IllegalStateException} will be thrown. + * </p> + * <dd><b>Update</b></dd> + * <p> + * Updates can only be performed for individual records on {@link ElementaryFiles#EF_ADN}. + * A specific record is referenced via the Uri returned by + * {@link SimRecords#getItemUri(int, int, int)}. Updates have the same constraints and + * behavior for the {@link SimRecords#PHONE_NUMBER} and {@link SimRecords#NAME} as insert. + * However, in the case of update the {@link SimRecords#PHONE_NUMBER} may be omitted as + * the existing record will already have a valid value. + * </p> + * <dd><b>Delete</b></dd> + * <p> + * Delete may only be performed for individual records on {@link ElementaryFiles#EF_ADN}. + * Deleting records will free up space for use by future inserts. + * </p> + * <dd><b>Query</b></dd> + * <p> + * All the records stored on a specific elementary file can be read via a Uri returned by + * {@link SimRecords#getContentUri(int, int)}. This query always returns all records; there + * is no support for filtering via a selection. An individual record can be queried via a Uri + * returned by {@link SimRecords#getItemUri(int, int, int)}. Queries will throw an + * {@link IllegalArgumentException} when the SIM with the subscription ID or the elementary file + * type are invalid or unavailable. + * </p> + * </dl> + */ public static final class SimRecords { /** @@ -197,8 +266,8 @@ public final class SimPhonebookContract { * be discovered by querying {@link ElementaryFiles#CONTENT_URI}. * * <p>If a SIM with the provided subscription ID does not exist or the SIM with the provided - * subscription ID doesn't support the specified entity file then queries will return - * and empty cursor and inserts will throw an {@link IllegalArgumentException} + * subscription ID doesn't support the specified entity file then all operations will + * throw an {@link IllegalArgumentException}. * * @param subscriptionId the subscriptionId of the SIM card that this Uri will reference * @param efType the elementary file on the SIM that this Uri will reference @@ -233,6 +302,9 @@ public final class SimPhonebookContract { * must be greater than 0. If there is no record with this record * number in the specified entity file then it will be treated as a * non-existent record. + * @see ElementaryFiles#SUBSCRIPTION_ID + * @see ElementaryFiles#EF_TYPE + * @see #RECORD_NUMBER */ @NonNull public static Uri getItemUri( @@ -287,7 +359,28 @@ public final class SimPhonebookContract { } - /** Constants for metadata about the elementary files of the SIM cards in the phone. */ + /** + * Constants for metadata about the elementary files of the SIM cards in the phone. + * + * <h3>Operations </h3> + * <dl> + * <dd><b>Insert</b></dd> + * <p>Insert is not supported for the Uris defined in this class.</p> + * <dd><b>Update</b></dd> + * <p>Update is not supported for the Uris defined in this class.</p> + * <dd><b>Delete</b></dd> + * <p>Delete is not supported for the Uris defined in this class.</p> + * <dd><b>Query</b></dd> + * <p> + * The elementary files for all the inserted SIMs can be read via + * {@link ElementaryFiles#CONTENT_URI}. Unsupported elementary files are omitted from the + * results. This Uri always returns all supported elementary files for all available SIMs; it + * does not support filtering via a selection. A specific elementary file can be queried + * via a Uri returned by {@link ElementaryFiles#getItemUri(int, int)}. If the elementary file + * referenced by this Uri is unsupported by the SIM then the query will return an empty cursor. + * </p> + * </dl> + */ public static final class ElementaryFiles { /** {@link SubscriptionInfo#getSimSlotIndex()} of the SIM for this row. */ diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index b22921233f05..2b577d04b18d 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -46,6 +46,9 @@ public class FeatureFlagUtils { "settings_do_not_restore_preserved"; /** @hide */ public static final String SETTINGS_PROVIDER_MODEL = "settings_provider_model"; + /** @hide */ + public static final String SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES + = "settings_use_new_backup_eligibility_rules"; private static final Map<String, String> DEFAULT_FLAGS; @@ -68,6 +71,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_silky_home", "false"); DEFAULT_FLAGS.put("settings_contextual_home", "false"); DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "false"); + DEFAULT_FLAGS.put(SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES, "false"); } private static final Set<String> PERSISTENT_FLAGS; diff --git a/core/java/android/util/RotationUtils.java b/core/java/android/util/RotationUtils.java index 698cb77947cf..0ac2c9c25ad1 100644 --- a/core/java/android/util/RotationUtils.java +++ b/core/java/android/util/RotationUtils.java @@ -24,6 +24,7 @@ import static android.view.Surface.ROTATION_90; import android.annotation.Dimension; import android.graphics.Insets; import android.graphics.Matrix; +import android.graphics.Rect; import android.view.Surface.Rotation; /** @@ -73,6 +74,60 @@ public class RotationUtils { } /** + * Rotates bounds as if parentBounds and bounds are a group. The group is rotated from + * oldRotation to newRotation. This assumes that parentBounds is at 0,0 and remains at 0,0 after + * rotation. The bounds will be at the same physical position in parentBounds. + * + * Only 'inOutBounds' is mutated. + */ + public static void rotateBounds(Rect inOutBounds, Rect parentBounds, @Rotation int oldRotation, + @Rotation int newRotation) { + rotateBounds(inOutBounds, parentBounds, deltaRotation(oldRotation, newRotation)); + } + + /** + * Rotates bounds as if parentBounds and bounds are a group. The group is rotated by `delta` + * 90-degree counter-clockwise increments. This assumes that parentBounds is at 0,0 and + * remains at 0,0 after rotation. The bounds will be at the same physical position in + * parentBounds. + * + * Only 'inOutBounds' is mutated. + */ + public static void rotateBounds(Rect inOutBounds, Rect parentBounds, @Rotation int rotation) { + final int origLeft = inOutBounds.left; + final int origTop = inOutBounds.top; + switch (rotation) { + case ROTATION_0: + return; + case ROTATION_90: + inOutBounds.left = inOutBounds.top; + inOutBounds.top = parentBounds.right - inOutBounds.right; + inOutBounds.right = inOutBounds.bottom; + inOutBounds.bottom = parentBounds.right - origLeft; + return; + case ROTATION_180: + inOutBounds.left = parentBounds.right - inOutBounds.right; + inOutBounds.right = parentBounds.right - origLeft; + inOutBounds.top = parentBounds.bottom - inOutBounds.bottom; + inOutBounds.bottom = parentBounds.bottom - origTop; + return; + case ROTATION_270: + inOutBounds.left = parentBounds.bottom - inOutBounds.bottom; + inOutBounds.bottom = inOutBounds.right; + inOutBounds.right = parentBounds.bottom - inOutBounds.top; + inOutBounds.top = origLeft; + } + } + + /** @return the rotation needed to rotate from oldRotation to newRotation. */ + @Rotation + public static int deltaRotation(int oldRotation, int newRotation) { + int delta = newRotation - oldRotation; + if (delta < 0) delta += 4; + return delta; + } + + /** * Sets a matrix such that given a rotation, it transforms physical display * coordinates to that rotation's logical coordinates. * diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl index b9c55081a103..468a69c7bddb 100644 --- a/core/java/android/uwb/IUwbAdapter.aidl +++ b/core/java/android/uwb/IUwbAdapter.aidl @@ -17,7 +17,6 @@ package android.uwb; import android.os.PersistableBundle; -import android.uwb.AngleOfArrivalSupport; import android.uwb.IUwbAdapterStateCallbacks; import android.uwb.IUwbRangingCallbacks; import android.uwb.SessionHandle; @@ -47,43 +46,6 @@ interface IUwbAdapter { void unregisterAdapterStateCallbacks(in IUwbAdapterStateCallbacks callbacks); /** - * Returns true if ranging is supported, false otherwise - */ - boolean isRangingSupported(); - - /** - * Get the angle of arrival supported by this device - * - * @return the angle of arrival type supported - */ - AngleOfArrivalSupport getAngleOfArrivalSupport(); - - /** - * Generates a list of the supported 802.15.4z channels - * - * The list must be prioritized in the order of preferred channel usage. - * - * The list must only contain channels that are permitted to be used in the - * device's current location. - * - * @return an array of support channels on the device for the current location. - */ - int[] getSupportedChannels(); - - /** - * Generates a list of the supported 802.15.4z preamble codes - * - * The list must be prioritized in the order of preferred preamble usage. - * - * The list must only contain preambles that are permitted to be used in the - * device's current location. - * - * @return an array of supported preambles on the device for the current - * location. - */ - int[] getSupportedPreambleCodes(); - - /** * Get the accuracy of the ranging timestamps * * @return accuracy of the ranging timestamps in nanoseconds @@ -91,27 +53,6 @@ interface IUwbAdapter { long getTimestampResolutionNanos(); /** - * Get the supported number of simultaneous ranging sessions - * - * @return the supported number of simultaneous ranging sessions - */ - int getMaxSimultaneousSessions(); - - /** - * Get the maximum number of remote devices per session when local device is initiator - * - * @return the maximum number of remote devices supported in a single session - */ - int getMaxRemoteDevicesPerInitiatorSession(); - - /** - * Get the maximum number of remote devices per session when local device is responder - * - * @return the maximum number of remote devices supported in a single session - */ - int getMaxRemoteDevicesPerResponderSession(); - - /** * Provides the capabilities and features of the device * * @return specification specific capabilities and features of the device diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java index 2dc0ba0b9b80..63a6d058f358 100644 --- a/core/java/android/uwb/UwbManager.java +++ b/core/java/android/uwb/UwbManager.java @@ -32,10 +32,6 @@ import android.os.ServiceManager; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; import java.util.concurrent.Executor; /** @@ -195,133 +191,6 @@ public final class UwbManager { } /** - * Check if ranging is supported, regardless of ranging method - * - * @return true if ranging is supported - */ - @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) - public boolean isRangingSupported() { - try { - return mUwbAdapter.isRangingSupported(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * @hide - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { - ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE, - ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D, - ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL, - ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL}) - public @interface AngleOfArrivalSupportType {} - - /** - * Indicate absence of support for angle of arrival measurement - */ - public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE = 1; - - /** - * Indicate support for planar angle of arrival measurement, due to antenna - * limitation. Typically requires at least two antennas. - */ - public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D = 2; - - /** - * Indicate support for three dimensional angle of arrival measurement. - * Typically requires at least three antennas. However, due to antenna - * arrangement, a platform may only support hemi-spherical azimuth angles - * ranging from -pi/2 to pi/2 - */ - public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL = 3; - - /** - * Indicate support for three dimensional angle of arrival measurement. - * Typically requires at least three antennas. This mode supports full - * azimuth angles ranging from -pi to pi. - */ - public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL = 4; - - /** - * Gets the {@link AngleOfArrivalSupportType} supported on this platform - * <p>Possible return values are - * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE}, - * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D}, - * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL}, - * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL}. - * - * @return angle of arrival type supported - */ - @AngleOfArrivalSupportType - @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) - public int getAngleOfArrivalSupport() { - try { - switch (mUwbAdapter.getAngleOfArrivalSupport()) { - case AngleOfArrivalSupport.TWO_DIMENSIONAL: - return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D; - - case AngleOfArrivalSupport.THREE_DIMENSIONAL_HEMISPHERICAL: - return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL; - - case AngleOfArrivalSupport.THREE_DIMENSIONAL_SPHERICAL: - return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL; - - case AngleOfArrivalSupport.NONE: - default: - return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE; - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Get a {@link List} of supported channel numbers based on the device's current location - * <p>The returned values are ordered by the system's desired ordered of use, with the first - * entry being the most preferred. - * - * <p>Channel numbers are defined based on the IEEE 802.15.4z standard for UWB. - * - * @return {@link List} of supported channel numbers ordered by preference - */ - @NonNull - @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) - public List<Integer> getSupportedChannelNumbers() { - List<Integer> channels = new ArrayList<>(); - try { - for (int channel : mUwbAdapter.getSupportedChannels()) { - channels.add(channel); - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - return channels; - } - - /** - * Get a {@link List} of supported preamble code indices - * <p> Preamble code indices are defined based on the IEEE 802.15.4z standard for UWB. - * - * @return {@link List} of supported preamble code indices - */ - @NonNull - @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) - public Set<Integer> getSupportedPreambleCodeIndices() { - Set<Integer> preambles = new HashSet<>(); - try { - for (int preamble : mUwbAdapter.getSupportedPreambleCodes()) { - preambles.add(preamble); - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - return preambles; - } - - /** * Get the timestamp resolution for events in nanoseconds * <p>This value defines the maximum error of all timestamps for events reported to * {@link RangingSession.Callback}. @@ -339,50 +208,6 @@ public final class UwbManager { } /** - * Get the number of simultaneous sessions allowed in the system - * - * @return the maximum allowed number of simultaneously open {@link RangingSession} instances. - */ - @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) - public int getMaxSimultaneousSessions() { - try { - return mUwbAdapter.getMaxSimultaneousSessions(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Get the maximum number of remote devices in a {@link RangingSession} when the local device - * is the initiator. - * - * @return the maximum number of remote devices per {@link RangingSession} - */ - @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) - public int getMaxRemoteDevicesPerInitiatorSession() { - try { - return mUwbAdapter.getMaxRemoteDevicesPerInitiatorSession(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Get the maximum number of remote devices in a {@link RangingSession} when the local device - * is a responder. - * - * @return the maximum number of remote devices per {@link RangingSession} - */ - @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) - public int getMaxRemoteDevicesPerResponderSession() { - try { - return mUwbAdapter.getMaxRemoteDevicesPerResponderSession(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** * Open a {@link RangingSession} with the given parameters * <p>The {@link RangingSession.Callback#onOpened(RangingSession)} function is called with a * {@link RangingSession} object used to control ranging when the session is successfully diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 8117c963b959..0ba1dfee16f3 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -34,7 +34,6 @@ import android.graphics.ColorSpace; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; -import android.hardware.display.DeviceProductInfo; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerGlobal; import android.os.Build; @@ -1182,18 +1181,6 @@ public final class Display { } /** - * Returns the product-specific information about the display or the directly connected - * device on the display chain. - * For example, if the display is transitively connected, this field may contain product - * information about the intermediate device. - * Returns {@code null} if product information is not available. - */ - @Nullable - public DeviceProductInfo getDeviceProductInfo() { - return mDisplayInfo.deviceProductInfo; - } - - /** * Gets display metrics that describe the size and density of this display. * The size returned by this method does not necessarily represent the * actual raw size (native resolution) of the display. diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java index 6543de15f969..eb49e52d5050 100644 --- a/core/java/android/view/FrameMetrics.java +++ b/core/java/android/view/FrameMetrics.java @@ -213,8 +213,7 @@ public final class FrameMetrics { Index.FRAME_TIMELINE_VSYNC_ID, Index.INTENDED_VSYNC, Index.VSYNC, - Index.OLDEST_INPUT_EVENT, - Index.NEWEST_INPUT_EVENT, + Index.INPUT_EVENT_ID, Index.HANDLE_INPUT_START, Index.ANIMATION_START, Index.PERFORM_TRAVERSALS_START, @@ -225,8 +224,11 @@ public final class FrameMetrics { Index.ISSUE_DRAW_COMMANDS_START, Index.SWAP_BUFFERS, Index.FRAME_COMPLETED, + Index.DEQUEUE_BUFFER_DURATION, + Index.QUEUE_BUFFER_DURATION, Index.GPU_COMPLETED, - Index.SWAP_BUFFERS_COMPLETED + Index.SWAP_BUFFERS_COMPLETED, + Index.DISPLAY_PRESENT_TIME, }) @Retention(RetentionPolicy.SOURCE) public @interface Index { @@ -234,20 +236,22 @@ public final class FrameMetrics { int FRAME_TIMELINE_VSYNC_ID = 1; int INTENDED_VSYNC = 2; int VSYNC = 3; - int OLDEST_INPUT_EVENT = 4; - int NEWEST_INPUT_EVENT = 5; - int HANDLE_INPUT_START = 6; - int ANIMATION_START = 7; - int PERFORM_TRAVERSALS_START = 8; - int DRAW_START = 9; - int FRAME_DEADLINE = 10; - int SYNC_QUEUED = 11; - int SYNC_START = 12; - int ISSUE_DRAW_COMMANDS_START = 13; - int SWAP_BUFFERS = 14; - int FRAME_COMPLETED = 15; - int GPU_COMPLETED = 18; - int SWAP_BUFFERS_COMPLETED = 19; + int INPUT_EVENT_ID = 4; + int HANDLE_INPUT_START = 5; + int ANIMATION_START = 6; + int PERFORM_TRAVERSALS_START = 7; + int DRAW_START = 8; + int FRAME_DEADLINE = 9; + int SYNC_QUEUED = 10; + int SYNC_START = 11; + int ISSUE_DRAW_COMMANDS_START = 12; + int SWAP_BUFFERS = 13; + int FRAME_COMPLETED = 14; + int DEQUEUE_BUFFER_DURATION = 15; + int QUEUE_BUFFER_DURATION = 16; + int GPU_COMPLETED = 17; + int SWAP_BUFFERS_COMPLETED = 18; + int DISPLAY_PRESENT_TIME = 19; int FRAME_STATS_COUNT = 20; // must always be last and in sync with // FrameInfoIndex::NumIndexes in libs/hwui/FrameInfo.h diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedTaskListener.aidl index 29c9c155e978..c31e67e59a99 100644 --- a/core/java/android/view/IPinnedStackListener.aidl +++ b/core/java/android/view/IPinnedTaskListener.aidl @@ -23,11 +23,11 @@ import android.graphics.Rect; import android.view.DisplayInfo; /** - * Listener for changes to the pinned stack made by the WindowManager. + * Listener for changes to the pinned task made by the WindowManager. * * @hide */ -oneway interface IPinnedStackListener { +oneway interface IPinnedTaskListener { /** * Called when the window manager has detected a change that would cause the movement bounds diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 4e4ba3fad246..afdf798d03ce 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -35,13 +35,12 @@ import android.os.ParcelFileDescriptor; import android.view.DisplayCutout; import android.view.IApplicationToken; import android.view.IAppTransitionAnimationSpecsFuture; -import android.view.IDockedStackListener; import android.view.IDisplayWindowInsetsController; import android.view.IDisplayWindowListener; import android.view.IDisplayFoldListener; import android.view.IDisplayWindowRotationController; import android.view.IOnKeyguardExitResult; -import android.view.IPinnedStackListener; +import android.view.IPinnedTaskListener; import android.view.IScrollCaptureCallbacks; import android.view.RemoteAnimationAdapter; import android.view.IRotationWatcher; @@ -446,12 +445,12 @@ interface IWindowManager * Sets the region the user can touch the divider. This region will be excluded from the region * which is used to cause a focus switch when dispatching touch. */ - void setDockedStackDividerTouchRegion(in Rect touchableRegion); + void setDockedTaskDividerTouchRegion(in Rect touchableRegion); /** - * Registers a listener that will be called when the pinned stack state changes. + * Registers a listener that will be called when the pinned task state changes. */ - void registerPinnedStackListener(int displayId, IPinnedStackListener listener); + void registerPinnedTaskListener(int displayId, IPinnedTaskListener listener); /** * Requests Keyboard Shortcuts from the displayed window. diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index 59e493191711..fafb885c58e0 100644 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -26,6 +26,7 @@ import android.hardware.Battery; import android.hardware.SensorManager; import android.hardware.input.InputDeviceIdentifier; import android.hardware.input.InputManager; +import android.hardware.lights.LightsManager; import android.os.Build; import android.os.NullVibrator; import android.os.Parcel; @@ -89,6 +90,9 @@ public final class InputDevice implements Parcelable { @GuardedBy("mMotionRanges") private Battery mBattery; + @GuardedBy("mMotionRanges") + private LightsManager mLightsManager; + /** * A mask for input source classes. * @@ -859,6 +863,21 @@ public final class InputDevice implements Parcelable { } /** + * Gets the lights manager associated with the device, if there is one. + * Even if the device does not have lights, the result is never null. + * Use {@link LightsManager#getLights} to determine whether any lights is + * present. + * + * @return The lights manager associated with the device, never null. + */ + public @NonNull LightsManager getLightsManager() { + if (mLightsManager == null) { + mLightsManager = InputManager.getInstance().getInputDeviceLightsManager(mId); + } + return mLightsManager; + } + + /** * Gets the sensor manager service associated with the input device. * Even if the device does not have a sensor, the result is never null. * Use {@link SensorManager#getSensorList} to get a full list of all supported sensors. diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 2bd32acc6c2c..0832578d80c5 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -191,9 +191,6 @@ public final class SurfaceControl implements Parcelable { private static native void nativeReparent(long transactionObj, long nativeObject, long newParentNativeObject); - private static native boolean nativeGetAutoLowLatencyModeSupport(IBinder displayToken); - private static native boolean nativeGetGameContentTypeSupport(IBinder displayToken); - private static native void nativeSetInputWindowInfo(long transactionObj, long nativeObject, InputWindowHandle handle); @@ -1750,6 +1747,9 @@ public final class SurfaceControl implements Parcelable { public Display.HdrCapabilities hdrCapabilities; + public boolean autoLowLatencyModeSupported; + public boolean gameContentTypeSupported; + @Override public String toString() { return "DynamicDisplayInfo{" @@ -1757,7 +1757,9 @@ public final class SurfaceControl implements Parcelable { + ", activeDisplayModeId=" + activeDisplayModeId + ", supportedColorModes=" + Arrays.toString(supportedColorModes) + ", activeColorMode=" + activeColorMode - + ", hdrCapabilities=" + hdrCapabilities + "}"; + + ", hdrCapabilities=" + hdrCapabilities + + ", autoLowLatencyModeSupported=" + autoLowLatencyModeSupported + + ", gameContentTypeSupported" + gameContentTypeSupported + "}"; } @Override @@ -2204,28 +2206,6 @@ public final class SurfaceControl implements Parcelable { /** * @hide */ - public static boolean getAutoLowLatencyModeSupport(IBinder displayToken) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); - } - - return nativeGetAutoLowLatencyModeSupport(displayToken); - } - - /** - * @hide - */ - public static boolean getGameContentTypeSupport(IBinder displayToken) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); - } - - return nativeGetGameContentTypeSupport(displayToken); - } - - /** - * @hide - */ @UnsupportedAppUsage public static IBinder createDisplay(String name, boolean secure) { if (name == null) { diff --git a/core/java/android/view/SurfaceControlFpsListener.java b/core/java/android/view/SurfaceControlFpsListener.java new file mode 100644 index 000000000000..517b0fb8ccd3 --- /dev/null +++ b/core/java/android/view/SurfaceControlFpsListener.java @@ -0,0 +1,93 @@ +/* + * 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 android.view; + +import android.annotation.NonNull; + +/** + * Listener for sampling the frames per second for a SurfaceControl and its children. + * This should only be used by a system component that needs to listen to a SurfaceControl's + * tree's FPS when it is not actively submitting transactions for that SurfaceControl. + * Otherwise, ASurfaceTransaction_OnComplete callbacks should be used. + * + * @hide + */ +public abstract class SurfaceControlFpsListener { + private long mNativeListener; + + public SurfaceControlFpsListener() { + mNativeListener = nativeCreate(this); + } + + protected void destroy() { + if (mNativeListener == 0) { + return; + } + unregister(); + nativeDestroy(mNativeListener); + mNativeListener = 0; + } + + @Override + protected void finalize() throws Throwable { + try { + destroy(); + } finally { + super.finalize(); + } + } + + /** + * Reports the fps from the registered SurfaceControl + */ + public abstract void onFpsReported(float fps); + + /** + * Registers the sampling listener. + */ + public void register(@NonNull SurfaceControl layer) { + if (mNativeListener == 0) { + return; + } + + nativeRegister(mNativeListener, layer.mNativeObject); + } + + /** + * Unregisters the sampling listener. + */ + public void unregister() { + if (mNativeListener == 0) { + return; + } + nativeUnregister(mNativeListener); + } + + /** + * Dispatch the collected sample. + * + * Called from native code on a binder thread. + */ + private static void dispatchOnFpsReported(SurfaceControlFpsListener listener, float fps) { + listener.onFpsReported(fps); + } + + private static native long nativeCreate(SurfaceControlFpsListener thiz); + private static native void nativeDestroy(long ptr); + private static native void nativeRegister(long ptr, long layerObject); + private static native void nativeUnregister(long ptr); +} diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index c46fbc7fb4c4..3789324a038c 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -6996,6 +6996,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #getScrollIndicators() * @attr ref android.R.styleable#View_scrollIndicators */ + @RemotableViewMethod public void setScrollIndicators(@ScrollIndicators int indicators) { setScrollIndicators(indicators, SCROLL_INDICATORS_PFLAG3_MASK >>> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT); @@ -11881,6 +11882,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #setFocusable(int) * @attr ref android.R.styleable#View_focusable */ + @RemotableViewMethod public void setFocusable(boolean focusable) { setFocusable(focusable ? FOCUSABLE : NOT_FOCUSABLE); } @@ -11899,6 +11901,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #setFocusableInTouchMode(boolean) * @attr ref android.R.styleable#View_focusable */ + @RemotableViewMethod public void setFocusable(@Focusable int focusable) { if ((focusable & (FOCUSABLE_AUTO | FOCUSABLE)) == 0) { setFlags(0, FOCUSABLE_IN_TOUCH_MODE); @@ -11917,6 +11920,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #setFocusable(boolean) * @attr ref android.R.styleable#View_focusableInTouchMode */ + @RemotableViewMethod public void setFocusableInTouchMode(boolean focusableInTouchMode) { // Focusable in touch mode should always be set before the focusable flag // otherwise, setting the focusable flag will trigger a focusableViewAvailable() @@ -12871,6 +12875,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_focusedByDefault */ + @RemotableViewMethod public void setFocusedByDefault(boolean isFocusedByDefault) { if (isFocusedByDefault == ((mPrivateFlags3 & PFLAG3_FOCUSED_BY_DEFAULT) != 0)) { return; @@ -16850,6 +16855,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_rotation */ + @RemotableViewMethod public void setRotation(float rotation) { if (rotation != getRotation()) { // Double-invalidation is necessary to capture view's old and new areas @@ -16896,6 +16902,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_rotationY */ + @RemotableViewMethod public void setRotationY(float rotationY) { if (rotationY != getRotationY()) { invalidateViewProperty(true, false); @@ -16941,6 +16948,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_rotationX */ + @RemotableViewMethod public void setRotationX(float rotationX) { if (rotationX != getRotationX()) { invalidateViewProperty(true, false); @@ -16978,6 +16986,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_scaleX */ + @RemotableViewMethod public void setScaleX(float scaleX) { if (scaleX != getScaleX()) { scaleX = sanitizeFloatPropertyValue(scaleX, "scaleX"); @@ -17016,6 +17025,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_scaleY */ + @RemotableViewMethod public void setScaleY(float scaleY) { if (scaleY != getScaleY()) { scaleY = sanitizeFloatPropertyValue(scaleY, "scaleY"); @@ -17061,6 +17071,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_transformPivotX */ + @RemotableViewMethod public void setPivotX(float pivotX) { if (!mRenderNode.isPivotExplicitlySet() || pivotX != getPivotX()) { invalidateViewProperty(true, false); @@ -17103,6 +17114,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_transformPivotY */ + @RemotableViewMethod public void setPivotY(float pivotY) { if (!mRenderNode.isPivotExplicitlySet() || pivotY != getPivotY()) { invalidateViewProperty(true, false); @@ -17243,6 +17255,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_alpha */ + @RemotableViewMethod public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) { ensureTransformationInfo(); if (mTransformationInfo.mAlpha != alpha) { @@ -17732,6 +17745,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_elevation */ + @RemotableViewMethod public void setElevation(float elevation) { if (elevation != getElevation()) { elevation = sanitizeFloatPropertyValue(elevation, "elevation"); @@ -17766,6 +17780,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_translationX */ + @RemotableViewMethod public void setTranslationX(float translationX) { if (translationX != getTranslationX()) { invalidateViewProperty(true, false); @@ -17801,6 +17816,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_translationY */ + @RemotableViewMethod public void setTranslationY(float translationY) { if (translationY != getTranslationY()) { invalidateViewProperty(true, false); @@ -17828,6 +17844,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_translationZ */ + @RemotableViewMethod public void setTranslationZ(float translationZ) { if (translationZ != getTranslationZ()) { translationZ = sanitizeFloatPropertyValue(translationZ, "translationZ"); @@ -23989,6 +24006,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #getBackgroundTintList() * @see Drawable#setTintList(ColorStateList) */ + @RemotableViewMethod public void setBackgroundTintList(@Nullable ColorStateList tint) { if (mBackgroundTint == null) { mBackgroundTint = new TintInfo(); @@ -24248,6 +24266,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #getForegroundTintList() * @see Drawable#setTintList(ColorStateList) */ + @RemotableViewMethod public void setForegroundTintList(@Nullable ColorStateList tint) { if (mForegroundInfo == null) { mForegroundInfo = new ForegroundInfo(); diff --git a/core/java/android/view/ViewFrameInfo.java b/core/java/android/view/ViewFrameInfo.java index 890d071f8090..d4aaa611f800 100644 --- a/core/java/android/view/ViewFrameInfo.java +++ b/core/java/android/view/ViewFrameInfo.java @@ -58,8 +58,8 @@ public class ViewFrameInfo { public void populateFrameInfo(FrameInfo frameInfo) { frameInfo.frameInfo[FrameInfo.FLAGS] |= flags; frameInfo.frameInfo[FrameInfo.DRAW_START] = drawStart; - frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT] = oldestInputEventTime; - frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT] = newestInputEventTime; + // TODO(b/169866723): Use InputEventAssigner + frameInfo.frameInfo[FrameInfo.INPUT_EVENT_ID] = newestInputEventTime; } /** diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index f1f6786aa43e..144691d3eaa0 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1677,7 +1677,8 @@ public final class ViewRootImpl implements ViewParent, // See comment for View.sForceLayoutWhenInsetsChanged if (View.sForceLayoutWhenInsetsChanged && mView != null - && mWindowAttributes.softInputMode == SOFT_INPUT_ADJUST_RESIZE) { + && (mWindowAttributes.softInputMode & SOFT_INPUT_MASK_ADJUST) + == SOFT_INPUT_ADJUST_RESIZE) { forceLayout(mView); } diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java index de4554b9e624..6ade5e622eab 100644 --- a/core/java/android/view/inputmethod/InputMethod.java +++ b/core/java/android/view/inputmethod/InputMethod.java @@ -105,7 +105,7 @@ public interface InputMethod { */ @MainThread default void initializeInternal(IBinder token, int displayId, - IInputMethodPrivilegedOperations privilegedOperations) { + IInputMethodPrivilegedOperations privilegedOperations, int configChanges) { updateInputMethodDisplay(displayId); attachToken(token); } diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java index 5d876a6f62d3..25712f8bf9b8 100644 --- a/core/java/android/view/inputmethod/InputMethodInfo.java +++ b/core/java/android/view/inputmethod/InputMethodInfo.java @@ -18,19 +18,23 @@ package android.view.inputmethod; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; +import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; +import android.inputmethodservice.InputMethodService; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; @@ -60,6 +64,7 @@ import java.util.List; * @attr ref android.R.styleable#InputMethod_isDefault * @attr ref android.R.styleable#InputMethod_supportsSwitchingToNextInputMethod * @attr ref android.R.styleable#InputMethod_supportsInlineSuggestions + * @attr ref android.R.styleable#InputMethod_configChanges */ public final class InputMethodInfo implements Parcelable { static final String TAG = "InputMethodInfo"; @@ -118,6 +123,12 @@ public final class InputMethodInfo implements Parcelable { private final boolean mInlineSuggestionsEnabled; /** + * The flag for configurations IME assumes the responsibility for handling in + * {@link InputMethodService#onConfigurationChanged(Configuration)}}. + */ + private final int mHandledConfigChanges; + + /** * @param service the {@link ResolveInfo} corresponds in which the IME is implemented. * @return a unique ID to be returned by {@link #getId()}. We have used * {@link ComponentName#flattenToShortString()} for this purpose (and it is already @@ -203,6 +214,8 @@ public final class InputMethodInfo implements Parcelable { false); inlineSuggestionsEnabled = sa.getBoolean( com.android.internal.R.styleable.InputMethod_supportsInlineSuggestions, false); + mHandledConfigChanges = sa.getInt( + com.android.internal.R.styleable.InputMethod_configChanges, 0); sa.recycle(); final int depth = parser.getDepth(); @@ -287,6 +300,7 @@ public final class InputMethodInfo implements Parcelable { mIsVrOnly = source.readBoolean(); mService = ResolveInfo.CREATOR.createFromParcel(source); mSubtypes = new InputMethodSubtypeArray(source); + mHandledConfigChanges = source.readInt(); mForceDefault = false; } @@ -298,7 +312,22 @@ public final class InputMethodInfo implements Parcelable { this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */, settingsActivity, null /* subtypes */, 0 /* isDefaultResId */, false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */, - false /* inlineSuggestionsEnabled */, false /* isVrOnly */); + false /* inlineSuggestionsEnabled */, false /* isVrOnly */, + 0 /* handledConfigChanges */); + } + + /** + * Temporary API for creating a built-in input method for test. + * @hide + */ + @TestApi + public InputMethodInfo(@NonNull String packageName, @NonNull String className, + @NonNull CharSequence label, @NonNull String settingsActivity, + int handledConfigChanges) { + this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */, + settingsActivity, null /* subtypes */, 0 /* isDefaultResId */, + false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */, + false /* inlineSuggestionsEnabled */, false /* isVrOnly */, handledConfigChanges); } /** @@ -310,7 +339,7 @@ public final class InputMethodInfo implements Parcelable { boolean forceDefault) { this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault, true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */, - false /* isVrOnly */); + false /* isVrOnly */, 0 /* handledconfigChanges */); } /** @@ -321,7 +350,8 @@ public final class InputMethodInfo implements Parcelable { List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault, boolean supportsSwitchingToNextInputMethod, boolean isVrOnly) { this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault, - supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly); + supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly, + 0 /* handledConfigChanges */); } /** @@ -331,7 +361,7 @@ public final class InputMethodInfo implements Parcelable { public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault, boolean supportsSwitchingToNextInputMethod, boolean inlineSuggestionsEnabled, - boolean isVrOnly) { + boolean isVrOnly, int handledConfigChanges) { final ServiceInfo si = ri.serviceInfo; mService = ri; mId = new ComponentName(si.packageName, si.name).flattenToShortString(); @@ -343,6 +373,7 @@ public final class InputMethodInfo implements Parcelable { mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod; mInlineSuggestionsEnabled = inlineSuggestionsEnabled; mIsVrOnly = isVrOnly; + mHandledConfigChanges = handledConfigChanges; } private static ResolveInfo buildFakeResolveInfo(String packageName, String className, @@ -489,6 +520,17 @@ public final class InputMethodInfo implements Parcelable { } } + /** + * Returns the bit mask of kinds of configuration changes that this IME + * can handle itself (without being restarted by the system). + * + * @attr ref android.R.styleable#InputMethod_configChanges + */ + @ActivityInfo.Config + public int getConfigChanges() { + return mHandledConfigChanges; + } + public void dump(Printer pw, String prefix) { pw.println(prefix + "mId=" + mId + " mSettingsActivityName=" + mSettingsActivityName @@ -579,6 +621,7 @@ public final class InputMethodInfo implements Parcelable { dest.writeBoolean(mIsVrOnly); mService.writeToParcel(dest, flags); mSubtypes.writeToParcel(dest); + dest.writeInt(mHandledConfigChanges); } /** diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 05b177ebbb45..39c09b46f2e1 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -6612,6 +6612,27 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } /** + * Returns the {@link EdgeEffect#getType()} for the edge effects. + * @return the {@link EdgeEffect#getType()} for the edge effects. + * @attr ref android.R.styleable#EdgeEffect_edgeEffectType + */ + @EdgeEffect.EdgeEffectType + public int getEdgeEffectType() { + return mEdgeGlowTop.getType(); + } + + /** + * Sets the {@link EdgeEffect#setType(int)} for the edge effects. + * @param type The edge effect type to use for the edge effects. + * @attr ref android.R.styleable#EdgeEffect_edgeEffectType + */ + public void setEdgeEffectType(@EdgeEffect.EdgeEffectType int type) { + mEdgeGlowTop.setType(type); + mEdgeGlowBottom.setType(type); + invalidate(); + } + + /** * Sets the recycler listener to be notified whenever a View is set aside in * the recycler for later reuse. This listener can be used to free resources * associated to the View. diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java index 93b2d8ae3c9a..34fe51e82e8f 100644 --- a/core/java/android/widget/AnalogClock.java +++ b/core/java/android/widget/AnalogClock.java @@ -23,8 +23,10 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.TypedArray; +import android.graphics.BlendMode; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; @@ -68,12 +70,16 @@ public class AnalogClock extends View { @UnsupportedAppUsage private Drawable mHourHand; + private final TintInfo mHourHandTintInfo = new TintInfo(); @UnsupportedAppUsage private Drawable mMinuteHand; + private final TintInfo mMinuteHandTintInfo = new TintInfo(); @Nullable private Drawable mSecondHand; + private final TintInfo mSecondHandTintInfo = new TintInfo(); @UnsupportedAppUsage private Drawable mDial; + private final TintInfo mDialTintInfo = new TintInfo(); private int mDialWidth; private int mDialHeight; @@ -111,18 +117,86 @@ public class AnalogClock extends View { mDial = context.getDrawable(com.android.internal.R.drawable.clock_dial); } + ColorStateList dialTintList = a.getColorStateList( + com.android.internal.R.styleable.AnalogClock_dialTint); + if (dialTintList != null) { + mDialTintInfo.mTintList = dialTintList; + mDialTintInfo.mHasTintList = true; + } + BlendMode dialTintMode = Drawable.parseBlendMode( + a.getInt(com.android.internal.R.styleable.AnalogClock_dialTintMode, -1), + null); + if (dialTintMode != null) { + mDialTintInfo.mTintBlendMode = dialTintMode; + mDialTintInfo.mHasTintBlendMode = true; + } + if (mDialTintInfo.mHasTintList || mDialTintInfo.mHasTintBlendMode) { + mDial = mDialTintInfo.apply(mDial); + } + mHourHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_hour); if (mHourHand == null) { mHourHand = context.getDrawable(com.android.internal.R.drawable.clock_hand_hour); } + ColorStateList hourHandTintList = a.getColorStateList( + com.android.internal.R.styleable.AnalogClock_hand_hourTint); + if (hourHandTintList != null) { + mHourHandTintInfo.mTintList = hourHandTintList; + mHourHandTintInfo.mHasTintList = true; + } + BlendMode hourHandTintMode = Drawable.parseBlendMode( + a.getInt(com.android.internal.R.styleable.AnalogClock_hand_hourTintMode, -1), + null); + if (hourHandTintMode != null) { + mHourHandTintInfo.mTintBlendMode = hourHandTintMode; + mHourHandTintInfo.mHasTintBlendMode = true; + } + if (mHourHandTintInfo.mHasTintList || mHourHandTintInfo.mHasTintBlendMode) { + mHourHand = mHourHandTintInfo.apply(mHourHand); + } + mMinuteHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_minute); if (mMinuteHand == null) { mMinuteHand = context.getDrawable(com.android.internal.R.drawable.clock_hand_minute); } + ColorStateList minuteHandTintList = a.getColorStateList( + com.android.internal.R.styleable.AnalogClock_hand_minuteTint); + if (minuteHandTintList != null) { + mMinuteHandTintInfo.mTintList = minuteHandTintList; + mMinuteHandTintInfo.mHasTintList = true; + } + BlendMode minuteHandTintMode = Drawable.parseBlendMode( + a.getInt(com.android.internal.R.styleable.AnalogClock_hand_minuteTintMode, -1), + null); + if (minuteHandTintMode != null) { + mMinuteHandTintInfo.mTintBlendMode = minuteHandTintMode; + mMinuteHandTintInfo.mHasTintBlendMode = true; + } + if (mMinuteHandTintInfo.mHasTintList || mMinuteHandTintInfo.mHasTintBlendMode) { + mMinuteHand = mMinuteHandTintInfo.apply(mMinuteHand); + } + mSecondHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_second); + ColorStateList secondHandTintList = a.getColorStateList( + com.android.internal.R.styleable.AnalogClock_hand_secondTint); + if (secondHandTintList != null) { + mSecondHandTintInfo.mTintList = secondHandTintList; + mSecondHandTintInfo.mHasTintList = true; + } + BlendMode secondHandTintMode = Drawable.parseBlendMode( + a.getInt(com.android.internal.R.styleable.AnalogClock_hand_secondTintMode, -1), + null); + if (secondHandTintMode != null) { + mSecondHandTintInfo.mTintBlendMode = secondHandTintMode; + mSecondHandTintInfo.mHasTintBlendMode = true; + } + if (mSecondHandTintInfo.mHasTintList || mSecondHandTintInfo.mHasTintBlendMode) { + mSecondHand = mSecondHandTintInfo.apply(mSecondHand); + } + mTimeZone = toZoneId(a.getString(com.android.internal.R.styleable.AnalogClock_timeZone)); createClock(); @@ -141,6 +215,68 @@ public class AnalogClock extends View { invalidate(); } + /** + * Applies a tint to the dial drawable. + * <p> + * Subsequent calls to {@link #setDial(Icon)} will + * automatically mutate the drawable and apply the specified tint and tint + * mode using {@link Drawable#setTintList(ColorStateList)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * + * @attr ref android.R.styleable#AnalogClock_dialTint + * @see #getDialTintList() + * @see Drawable#setTintList(ColorStateList) + */ + @RemotableViewMethod + public void setDialTintList(@Nullable ColorStateList tint) { + mDialTintInfo.mTintList = tint; + mDialTintInfo.mHasTintList = true; + + mDial = mDialTintInfo.apply(mDial); + } + + /** + * @return the tint applied to the dial drawable + * @attr ref android.R.styleable#AnalogClock_dialTint + * @see #setDialTintList(ColorStateList) + */ + @InspectableProperty(attributeId = com.android.internal.R.styleable.AnalogClock_dialTint) + @Nullable + public ColorStateList getDialTintList() { + return mDialTintInfo.mTintList; + } + + /** + * Specifies the blending mode used to apply the tint specified by + * {@link #setDialTintList(ColorStateList)}} to the dial drawable. + * The default mode is {@link BlendMode#SRC_IN}. + * + * @param blendMode the blending mode used to apply the tint, may be + * {@code null} to clear tint + * @attr ref android.R.styleable#AnalogClock_dialTintMode + * @see #getDialTintBlendMode() + * @see Drawable#setTintBlendMode(BlendMode) + */ + @RemotableViewMethod + public void setDialTintBlendMode(@Nullable BlendMode blendMode) { + mDialTintInfo.mTintBlendMode = blendMode; + mDialTintInfo.mHasTintBlendMode = true; + + mDial = mDialTintInfo.apply(mDial); + } + + /** + * @return the blending mode used to apply the tint to the dial drawable + * @attr ref android.R.styleable#AnalogClock_dialTintMode + * @see #setDialTintBlendMode(BlendMode) + */ + @InspectableProperty(attributeId = com.android.internal.R.styleable.AnalogClock_dialTintMode) + @Nullable + public BlendMode getDialTintBlendMode() { + return mDialTintInfo.mTintBlendMode; + } + /** Sets the hour hand of the clock to the specified Icon. */ @RemotableViewMethod public void setHourHand(@NonNull Icon icon) { @@ -150,6 +286,71 @@ public class AnalogClock extends View { invalidate(); } + /** + * Applies a tint to the hour hand drawable. + * <p> + * Subsequent calls to {@link #setHourHand(Icon)} will + * automatically mutate the drawable and apply the specified tint and tint + * mode using {@link Drawable#setTintList(ColorStateList)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * + * @attr ref android.R.styleable#AnalogClock_hand_hourTint + * @see #getHourHandTintList() + * @see Drawable#setTintList(ColorStateList) + */ + @RemotableViewMethod + public void setHourHandTintList(@Nullable ColorStateList tint) { + mHourHandTintInfo.mTintList = tint; + mHourHandTintInfo.mHasTintList = true; + + mHourHand = mHourHandTintInfo.apply(mHourHand); + } + + /** + * @return the tint applied to the hour hand drawable + * @attr ref android.R.styleable#AnalogClock_hand_hourTint + * @see #setHourHandTintList(ColorStateList) + */ + @InspectableProperty( + attributeId = com.android.internal.R.styleable.AnalogClock_hand_hourTint + ) + @Nullable + public ColorStateList getHourHandTintList() { + return mHourHandTintInfo.mTintList; + } + + /** + * Specifies the blending mode used to apply the tint specified by + * {@link #setHourHandTintList(ColorStateList)}} to the hour hand drawable. + * The default mode is {@link BlendMode#SRC_IN}. + * + * @param blendMode the blending mode used to apply the tint, may be + * {@code null} to clear tint + * @attr ref android.R.styleable#AnalogClock_hand_hourTintMode + * @see #getHourHandTintBlendMode() + * @see Drawable#setTintBlendMode(BlendMode) + */ + @RemotableViewMethod + public void setHourHandTintBlendMode(@Nullable BlendMode blendMode) { + mHourHandTintInfo.mTintBlendMode = blendMode; + mHourHandTintInfo.mHasTintBlendMode = true; + + mHourHand = mHourHandTintInfo.apply(mHourHand); + } + + /** + * @return the blending mode used to apply the tint to the hour hand drawable + * @attr ref android.R.styleable#AnalogClock_hand_hourTintMode + * @see #setHourHandTintBlendMode(BlendMode) + */ + @InspectableProperty( + attributeId = com.android.internal.R.styleable.AnalogClock_hand_hourTintMode) + @Nullable + public BlendMode getHourHandTintBlendMode() { + return mHourHandTintInfo.mTintBlendMode; + } + /** Sets the minute hand of the clock to the specified Icon. */ @RemotableViewMethod public void setMinuteHand(@NonNull Icon icon) { @@ -160,6 +361,71 @@ public class AnalogClock extends View { } /** + * Applies a tint to the minute hand drawable. + * <p> + * Subsequent calls to {@link #setMinuteHand(Icon)} will + * automatically mutate the drawable and apply the specified tint and tint + * mode using {@link Drawable#setTintList(ColorStateList)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * + * @attr ref android.R.styleable#AnalogClock_hand_minuteTint + * @see #getMinuteHandTintList() + * @see Drawable#setTintList(ColorStateList) + */ + @RemotableViewMethod + public void setMinuteHandTintList(@Nullable ColorStateList tint) { + mMinuteHandTintInfo.mTintList = tint; + mMinuteHandTintInfo.mHasTintList = true; + + mMinuteHand = mMinuteHandTintInfo.apply(mMinuteHand); + } + + /** + * @return the tint applied to the minute hand drawable + * @attr ref android.R.styleable#AnalogClock_hand_minuteTint + * @see #setMinuteHandTintList(ColorStateList) + */ + @InspectableProperty( + attributeId = com.android.internal.R.styleable.AnalogClock_hand_minuteTint + ) + @Nullable + public ColorStateList getMinuteHandTintList() { + return mMinuteHandTintInfo.mTintList; + } + + /** + * Specifies the blending mode used to apply the tint specified by + * {@link #setMinuteHandTintList(ColorStateList)}} to the minute hand drawable. + * The default mode is {@link BlendMode#SRC_IN}. + * + * @param blendMode the blending mode used to apply the tint, may be + * {@code null} to clear tint + * @attr ref android.R.styleable#AnalogClock_hand_minuteTintMode + * @see #getMinuteHandTintBlendMode() + * @see Drawable#setTintBlendMode(BlendMode) + */ + @RemotableViewMethod + public void setMinuteHandTintBlendMode(@Nullable BlendMode blendMode) { + mMinuteHandTintInfo.mTintBlendMode = blendMode; + mMinuteHandTintInfo.mHasTintBlendMode = true; + + mMinuteHand = mMinuteHandTintInfo.apply(mMinuteHand); + } + + /** + * @return the blending mode used to apply the tint to the minute hand drawable + * @attr ref android.R.styleable#AnalogClock_hand_minuteTintMode + * @see #setMinuteHandTintBlendMode(BlendMode) + */ + @InspectableProperty( + attributeId = com.android.internal.R.styleable.AnalogClock_hand_minuteTintMode) + @Nullable + public BlendMode getMinuteHandTintBlendMode() { + return mMinuteHandTintInfo.mTintBlendMode; + } + + /** * Sets the second hand of the clock to the specified Icon, or hides the second hand if it is * null. */ @@ -173,6 +439,71 @@ public class AnalogClock extends View { } /** + * Applies a tint to the second hand drawable. + * <p> + * Subsequent calls to {@link #setSecondHand(Icon)} will + * automatically mutate the drawable and apply the specified tint and tint + * mode using {@link Drawable#setTintList(ColorStateList)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * + * @attr ref android.R.styleable#AnalogClock_hand_secondTint + * @see #getSecondHandTintList() + * @see Drawable#setTintList(ColorStateList) + */ + @RemotableViewMethod + public void setSecondHandTintList(@Nullable ColorStateList tint) { + mSecondHandTintInfo.mTintList = tint; + mSecondHandTintInfo.mHasTintList = true; + + mSecondHand = mSecondHandTintInfo.apply(mSecondHand); + } + + /** + * @return the tint applied to the second hand drawable + * @attr ref android.R.styleable#AnalogClock_hand_secondTint + * @see #setSecondHandTintList(ColorStateList) + */ + @InspectableProperty( + attributeId = com.android.internal.R.styleable.AnalogClock_hand_secondTint + ) + @Nullable + public ColorStateList getSecondHandTintList() { + return mSecondHandTintInfo.mTintList; + } + + /** + * Specifies the blending mode used to apply the tint specified by + * {@link #setSecondHandTintList(ColorStateList)}} to the second hand drawable. + * The default mode is {@link BlendMode#SRC_IN}. + * + * @param blendMode the blending mode used to apply the tint, may be + * {@code null} to clear tint + * @attr ref android.R.styleable#AnalogClock_hand_secondTintMode + * @see #getSecondHandTintBlendMode() + * @see Drawable#setTintBlendMode(BlendMode) + */ + @RemotableViewMethod + public void setSecondHandTintBlendMode(@Nullable BlendMode blendMode) { + mSecondHandTintInfo.mTintBlendMode = blendMode; + mSecondHandTintInfo.mHasTintBlendMode = true; + + mSecondHand = mSecondHandTintInfo.apply(mSecondHand); + } + + /** + * @return the blending mode used to apply the tint to the second hand drawable + * @attr ref android.R.styleable#AnalogClock_hand_secondTintMode + * @see #setSecondHandTintBlendMode(BlendMode) + */ + @InspectableProperty( + attributeId = com.android.internal.R.styleable.AnalogClock_hand_secondTintMode) + @Nullable + public BlendMode getSecondHandTintBlendMode() { + return mSecondHandTintInfo.mTintBlendMode; + } + + /** * Indicates which time zone is currently used by this view. * * @return The ID of the current time zone or null if the default time zone, @@ -462,4 +793,36 @@ public class AnalogClock extends View { return null; } } + + private final class TintInfo { + boolean mHasTintList; + @Nullable ColorStateList mTintList; + boolean mHasTintBlendMode; + @Nullable BlendMode mTintBlendMode; + + /** + * Returns a mutated copy of {@code drawable} with tinting applied, or null if it's null. + */ + @Nullable + Drawable apply(@Nullable Drawable drawable) { + if (drawable == null) return null; + + Drawable newDrawable = drawable.mutate(); + + if (mHasTintList) { + newDrawable.setTintList(mTintList); + } + + if (mHasTintBlendMode) { + newDrawable.setTintBlendMode(mTintBlendMode); + } + + // All drawables should have the same state as the View itself. + if (drawable.isStateful()) { + newDrawable.setState(getDrawableState()); + } + + return newDrawable; + } + } } diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index 6dedd12a2730..23915e06335a 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -303,6 +303,27 @@ public class HorizontalScrollView extends FrameLayout { } /** + * Returns the {@link EdgeEffect#getType()} for the edge effects. + * @return the {@link EdgeEffect#getType()} for the edge effects. + * @attr ref android.R.styleable#EdgeEffect_edgeEffectType + */ + @EdgeEffect.EdgeEffectType + public int getEdgeEffectType() { + return mEdgeGlowLeft.getType(); + } + + /** + * Sets the {@link EdgeEffect#setType(int)} for the edge effects. + * @param type The edge effect type to use for the edge effects. + * @attr ref android.R.styleable#EdgeEffect_edgeEffectType + */ + public void setEdgeEffectType(@EdgeEffect.EdgeEffectType int type) { + mEdgeGlowRight.setType(type); + mEdgeGlowLeft.setType(type); + invalidate(); + } + + /** * @return The maximum amount this scroll view will scroll in response to * an arrow event. */ diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 0a08ccd34f02..8aa557bab4e3 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -648,6 +648,7 @@ public class ImageView extends View { * @see #getImageTintList() * @see Drawable#setTintList(ColorStateList) */ + @android.view.RemotableViewMethod public void setImageTintList(@Nullable ColorStateList tint) { mDrawableTintList = tint; mHasDrawableTint = true; diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index a44808eb3120..1b76ebf7c8c6 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -1308,6 +1308,7 @@ public class ProgressBar extends View { * @see #getSecondaryProgressTintList() * @see Drawable#setTintList(ColorStateList) */ + @RemotableViewMethod public void setSecondaryProgressTintList(@Nullable ColorStateList tint) { if (mProgressTintInfo == null) { mProgressTintInfo = new ProgressTintInfo(); @@ -1619,6 +1620,7 @@ public class ProgressBar extends View { * @param stateDescription The state description. */ @Override + @RemotableViewMethod public void setStateDescription(@Nullable CharSequence stateDescription) { mCustomStateDescription = stateDescription; if (stateDescription == null) { diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 30bf546eb83e..e0b4ec71b0a0 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -46,6 +46,8 @@ import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; +import android.content.res.loader.ResourcesLoader; +import android.content.res.loader.ResourcesProvider; import android.graphics.Bitmap; import android.graphics.BlendMode; import android.graphics.Outline; @@ -62,16 +64,19 @@ import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; import android.os.Parcel; +import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.os.Process; import android.os.StrictMode; import android.os.UserHandle; +import android.system.Os; import android.text.TextUtils; import android.util.ArrayMap; import android.util.DisplayMetrics; import android.util.IntArray; import android.util.Log; import android.util.Pair; +import android.util.SparseIntArray; import android.util.TypedValue; import android.util.TypedValue.ComplexDimensionUnit; import android.view.ContextThemeWrapper; @@ -92,6 +97,12 @@ import com.android.internal.R; import com.android.internal.util.ContrastColorUtil; import com.android.internal.util.Preconditions; +import java.io.ByteArrayOutputStream; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -224,35 +235,17 @@ public class RemoteViews implements Parcelable, Filter { }) @Retention(RetentionPolicy.SOURCE) public @interface MarginType {} - /** - * The value will apply to the marginLeft. - * @hide - */ + /** The value will apply to the marginLeft. */ public static final int MARGIN_LEFT = 0; - /** - * The value will apply to the marginTop. - * @hide - */ + /** The value will apply to the marginTop. */ public static final int MARGIN_TOP = 1; - /** - * The value will apply to the marginRight. - * @hide - */ + /** The value will apply to the marginRight. */ public static final int MARGIN_RIGHT = 2; - /** - * The value will apply to the marginBottom. - * @hide - */ + /** The value will apply to the marginBottom. */ public static final int MARGIN_BOTTOM = 3; - /** - * The value will apply to the marginStart. - * @hide - */ + /** The value will apply to the marginStart. */ public static final int MARGIN_START = 4; - /** - * The value will apply to the marginEnd. - * @hide - */ + /** The value will apply to the marginEnd. */ public static final int MARGIN_END = 5; /** @hide **/ @@ -538,8 +531,8 @@ public class RemoteViews implements Parcelable, Filter { * SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!! */ private abstract static class Action implements Parcelable { - public abstract void apply(View root, ViewGroup rootParent, - InteractionHandler handler) throws ActionException; + public abstract void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException; public static final int MERGE_REPLACE = 0; public static final int MERGE_APPEND = 1; @@ -569,8 +562,8 @@ public class RemoteViews implements Parcelable, Filter { * and return the final action which will run on the UI thread. * Override this if some of the tasks can be performed async. */ - public Action initActionAsync( - ViewTree root, ViewGroup rootParent, InteractionHandler handler) { + public Action initActionAsync(ViewTree root, ViewGroup rootParent, + InteractionHandler handler, ColorResources colorResources) { return this; } @@ -613,7 +606,9 @@ public class RemoteViews implements Parcelable, Filter { // Constant used during async execution. It is not parcelable. private static final Action ACTION_NOOP = new RuntimeAction() { @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { } + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { + } }; /** @@ -731,7 +726,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View view = root.findViewById(viewId); if (!(view instanceof AdapterView<?>)) return; @@ -766,7 +762,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, final InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, final InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; @@ -838,7 +835,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; @@ -861,7 +859,8 @@ public class RemoteViews implements Parcelable, Filter { if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) { ((RemoteViewsListAdapter) a).setViewsList(list); } else { - v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount)); + v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount, + colorResources)); } } else if (target instanceof AdapterViewAnimator) { AdapterViewAnimator v = (AdapterViewAnimator) target; @@ -869,7 +868,8 @@ public class RemoteViews implements Parcelable, Filter { if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) { ((RemoteViewsListAdapter) a).setViewsList(list); } else { - v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount)); + v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount, + colorResources)); } } } @@ -900,7 +900,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; @@ -937,7 +938,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public Action initActionAsync(ViewTree root, ViewGroup rootParent, - InteractionHandler handler) { + InteractionHandler handler, ColorResources colorResources) { SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(viewId, intent); copy.isAsync = true; return copy; @@ -976,7 +977,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, final InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, final InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; @@ -1055,7 +1057,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, final InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, final InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; if (!(target instanceof CompoundButton)) { @@ -1258,7 +1261,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; @@ -1315,7 +1319,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; @@ -1352,7 +1357,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View view = root.findViewById(viewId); if (view == null) return; @@ -1455,12 +1461,12 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, - InteractionHandler handler) throws ActionException { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException { ReflectionAction ra = new ReflectionAction(viewId, methodName, BaseReflectionAction.BITMAP, bitmap); - ra.apply(root, rootParent, handler); + ra.apply(root, rootParent, handler, colorResources); } @Override @@ -1536,7 +1542,8 @@ public class RemoteViews implements Parcelable, Filter { protected abstract Object getParameterValue(@Nullable View view) throws ActionException; @Override - public final void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public final void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View view = root.findViewById(viewId); if (view == null) return; @@ -1554,7 +1561,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public final Action initActionAsync(ViewTree root, ViewGroup rootParent, - InteractionHandler handler) { + InteractionHandler handler, ColorResources colorResources) { final View view = root.findViewById(viewId); if (view == null) return ACTION_NOOP; @@ -1956,7 +1963,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { mRunnable.run(); } } @@ -2011,7 +2019,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final Context context = root.getContext(); final ViewGroup target = root.findViewById(viewId); @@ -2020,12 +2029,14 @@ public class RemoteViews implements Parcelable, Filter { } // Inflate nested views and add as children - target.addView(mNestedViews.apply(context, target, handler), mIndex); + target.addView( + mNestedViews.apply(context, target, handler, null /* size */, colorResources), + mIndex); } @Override public Action initActionAsync(ViewTree root, ViewGroup rootParent, - InteractionHandler handler) { + InteractionHandler handler, ColorResources colorResources) { // In the async implementation, update the view tree so that subsequent calls to // findViewById return the current view. root.createTree(); @@ -2037,8 +2048,8 @@ public class RemoteViews implements Parcelable, Filter { // Inflate nested views and perform all the async tasks for the child remoteView. final Context context = root.mRoot.getContext(); - final AsyncApplyTask task = mNestedViews.getAsyncApplyTask( - context, targetVg, null, handler); + final AsyncApplyTask task = mNestedViews.getAsyncApplyTask(context, targetVg, + null /* listener */, handler, null /* size */, colorResources); final ViewTree tree = task.doInBackground(); if (tree == null) { @@ -2051,8 +2062,8 @@ public class RemoteViews implements Parcelable, Filter { return new RuntimeAction() { @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) - throws ActionException { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException { task.onPostExecute(tree); targetVg.addView(task.mResult, mIndex); } @@ -2113,7 +2124,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final ViewGroup target = root.findViewById(viewId); if (target == null) { @@ -2130,7 +2142,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public Action initActionAsync(ViewTree root, ViewGroup rootParent, - InteractionHandler handler) { + InteractionHandler handler, ColorResources colorResources) { // In the async implementation, update the view tree so that subsequent calls to // findViewById return the current view. root.createTree(); @@ -2154,8 +2166,8 @@ public class RemoteViews implements Parcelable, Filter { } return new RuntimeAction() { @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) - throws ActionException { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException { if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) { targetVg.removeAllViews(); return; @@ -2210,7 +2222,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null || target == root) { @@ -2225,7 +2238,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public Action initActionAsync(ViewTree root, ViewGroup rootParent, - InteractionHandler handler) { + InteractionHandler handler, ColorResources colorResources) { // In the async implementation, update the view tree so that subsequent calls to // findViewById return the correct view. root.createTree(); @@ -2244,8 +2257,8 @@ public class RemoteViews implements Parcelable, Filter { parent.mChildren.remove(target); return new RuntimeAction() { @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) - throws ActionException { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException { parentVg.removeView(target.mRoot); } }; @@ -2324,7 +2337,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final TextView target = root.findViewById(viewId); if (target == null) return; if (drawablesLoaded) { @@ -2355,7 +2369,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public Action initActionAsync(ViewTree root, ViewGroup rootParent, - InteractionHandler handler) { + InteractionHandler handler, ColorResources colorResources) { final TextView target = root.findViewById(viewId); if (target == null) return ACTION_NOOP; @@ -2433,7 +2447,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final TextView target = root.findViewById(viewId); if (target == null) return; target.setTextSize(units, size); @@ -2478,7 +2493,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; target.setPadding(left, top, right, bottom); @@ -2551,7 +2567,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) { return; @@ -2664,7 +2681,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; @@ -2699,7 +2717,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { // Let's traverse the viewtree and override all textColors! Stack<View> viewsToProcess = new Stack<>(); viewsToProcess.add(root); @@ -2749,7 +2768,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(mViewId); if (target == null) return; @@ -2783,7 +2803,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException { final View target = root.findViewById(viewId); if (target == null) return; @@ -2834,8 +2855,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) - throws ActionException { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException { final View target = root.findViewById(viewId); if (target == null) return; @@ -2911,8 +2932,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) - throws ActionException { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException { final View target = root.findViewById(viewId); if (target == null) return; @@ -4002,7 +4023,6 @@ public class RemoteViews implements Parcelable, Filter { * @param viewId The id of the view to change * @param type The margin being set e.g. {@link #MARGIN_END} * @param dimen a dimension resource to apply to the margin, or 0 to clear the margin. - * @hide */ public void setViewLayoutMarginDimen(@IdRes int viewId, @MarginType int type, @DimenRes int dimen) { @@ -4021,7 +4041,6 @@ public class RemoteViews implements Parcelable, Filter { * @param type The margin being set e.g. {@link #MARGIN_END} * @param value a value for the margin the given units. * @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP} - * @hide */ public void setViewLayoutMargin(@IdRes int viewId, @MarginType int type, float value, @ComplexDimensionUnit int units) { @@ -4039,7 +4058,6 @@ public class RemoteViews implements Parcelable, Filter { * * @param width Width of the view in the given units * @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP} - * @hide */ public void setViewLayoutWidth(@IdRes int viewId, float width, @ComplexDimensionUnit int units) { @@ -4051,7 +4069,6 @@ public class RemoteViews implements Parcelable, Filter { * the result of {@link Resources#getDimensionPixelSize(int)}. * * @param widthDimen the dimension resource for the view's width - * @hide */ public void setViewLayoutWidthDimen(@IdRes int viewId, @DimenRes int widthDimen) { addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, widthDimen)); @@ -4068,7 +4085,6 @@ public class RemoteViews implements Parcelable, Filter { * * @param height height of the view in the given units * @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP} - * @hide */ public void setViewLayoutHeight(@IdRes int viewId, float height, @ComplexDimensionUnit int units) { @@ -4080,7 +4096,6 @@ public class RemoteViews implements Parcelable, Filter { * the result of {@link Resources#getDimensionPixelSize(int)}. * * @param heightDimen a dimen resource to read the height from. - * @hide */ public void setViewLayoutHeightDimen(@IdRes int viewId, @DimenRes int heightDimen) { addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_HEIGHT, heightDimen)); @@ -4231,10 +4246,9 @@ public class RemoteViews implements Parcelable, Filter { * @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 to pass to the method. - * - * @hide */ - public void setColorStateList(@IdRes int viewId, String methodName, ColorStateList value) { + public void setColorStateList(@IdRes int viewId, @NonNull String methodName, + @Nullable ColorStateList value) { addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.COLOR_STATE_LIST, value)); } @@ -4687,7 +4701,7 @@ public class RemoteViews implements Parcelable, Filter { RemoteViews rvToApply = getRemoteViewsToApply(context, size); View result = inflateView(context, rvToApply, parent); - rvToApply.performApply(result, parent, handler); + rvToApply.performApply(result, parent, handler, null); return result; } @@ -4699,27 +4713,39 @@ public class RemoteViews implements Parcelable, Filter { /** @hide */ public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent, - @Nullable InteractionHandler handler, - @StyleRes int applyThemeResId, + @Nullable InteractionHandler handler, @StyleRes int applyThemeResId, @Nullable PointF size) { RemoteViews rvToApply = getRemoteViewsToApply(context, size); - View result = inflateView(context, rvToApply, parent, applyThemeResId); - rvToApply.performApply(result, parent, handler); + View result = inflateView(context, rvToApply, parent, applyThemeResId, null); + rvToApply.performApply(result, parent, handler, null); + return result; + } + + /** @hide */ + public View apply(Context context, ViewGroup parent, InteractionHandler handler, + @NonNull PointF size, @Nullable ColorResources colorResources) { + RemoteViews rvToApply = getRemoteViewsToApply(context, size); + + View result = inflateView(context, rvToApply, parent, 0, colorResources); + rvToApply.performApply(result, parent, handler, colorResources); return result; } private View inflateView(Context context, RemoteViews rv, ViewGroup parent) { - return inflateView(context, rv, parent, 0); + return inflateView(context, rv, parent, 0, null); } private View inflateView(Context context, RemoteViews rv, ViewGroup parent, - @StyleRes int applyThemeResId) { + @StyleRes int applyThemeResId, @Nullable ColorResources colorResources) { // RemoteViews may be built by an application installed in another // user. So build a context that loads resources from that user but // still returns the current users userId so settings like data / time formats // are loaded without requiring cross user persmissions. final Context contextForResources = getContextForResources(context); + if (colorResources != null) { + colorResources.apply(contextForResources); + } Context inflationContext = new RemoteViewsContextWrapper(context, contextForResources); // If mApplyThemeResId is not given, Theme.DeviceDefault will be used. @@ -4781,34 +4807,37 @@ public class RemoteViews implements Parcelable, Filter { */ public CancellationSignal applyAsync( Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener) { - return applyAsync(context, parent, executor, listener, null); + return applyAsync(context, parent, executor, listener, null /* handler */); } /** @hide */ public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener, InteractionHandler handler) { - return applyAsync(context, parent, executor, listener, handler, null); + return applyAsync(context, parent, executor, listener, handler, null /* size */); } /** @hide */ public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener, InteractionHandler handler, PointF size) { - return getAsyncApplyTask(context, parent, listener, handler, size).startTaskOnExecutor( - executor); + return getAsyncApplyTask(context, parent, listener, handler, size, null /* themeColors */) + .startTaskOnExecutor(executor); } - private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent, - OnViewAppliedListener listener, InteractionHandler handler) { - return getAsyncApplyTask(context, parent, listener, handler, null); + /** @hide */ + public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor, + OnViewAppliedListener listener, InteractionHandler handler, PointF size, + ColorResources colorResources) { + return getAsyncApplyTask(context, parent, listener, handler, size, colorResources) + .startTaskOnExecutor(executor); } private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent, - OnViewAppliedListener listener, InteractionHandler handler, PointF size) { - return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, - listener, - handler, null); + OnViewAppliedListener listener, InteractionHandler handler, PointF size, + ColorResources colorResources) { + return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener, + handler, colorResources, null /* result */); } private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree> @@ -4819,6 +4848,7 @@ public class RemoteViews implements Parcelable, Filter { final Context mContext; final OnViewAppliedListener mListener; final InteractionHandler mHandler; + final ColorResources mColorResources; private View mResult; private ViewTree mTree; @@ -4827,11 +4857,12 @@ public class RemoteViews implements Parcelable, Filter { private AsyncApplyTask( RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener, - InteractionHandler handler, View result) { + InteractionHandler handler, ColorResources colorResources, View result) { mRV = rv; mParent = parent; mContext = context; mListener = listener; + mColorResources = colorResources; mHandler = handler; mResult = result; @@ -4841,7 +4872,7 @@ public class RemoteViews implements Parcelable, Filter { protected ViewTree doInBackground(Void... params) { try { if (mResult == null) { - mResult = inflateView(mContext, mRV, mParent); + mResult = inflateView(mContext, mRV, mParent, 0, mColorResources); } mTree = new ViewTree(mResult); @@ -4850,7 +4881,8 @@ public class RemoteViews implements Parcelable, Filter { mActions = new Action[count]; for (int i = 0; i < count && !isCancelled(); i++) { // TODO: check if isCancelled in nested views. - mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler); + mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler, + mColorResources); } } else { mActions = null; @@ -4875,7 +4907,7 @@ public class RemoteViews implements Parcelable, Filter { InteractionHandler handler = mHandler == null ? DEFAULT_INTERACTION_HANDLER : mHandler; for (Action a : mActions) { - a.apply(viewTree.mRoot, mParent, handler); + a.apply(viewTree.mRoot, mParent, handler, mColorResources); } } } catch (Exception e) { @@ -4919,16 +4951,17 @@ public class RemoteViews implements Parcelable, Filter { * the {@link #apply(Context,ViewGroup)} call. */ public void reapply(Context context, View v) { - reapply(context, v, null, null); + reapply(context, v, null /* handler */); } /** @hide */ public void reapply(Context context, View v, InteractionHandler handler) { - reapply(context, v, handler, null); + reapply(context, v, handler, null /* size */, null /* colorResources */); } /** @hide */ - public void reapply(Context context, View v, InteractionHandler handler, PointF size) { + public void reapply(Context context, View v, InteractionHandler handler, PointF size, + ColorResources colorResources) { RemoteViews rvToApply = getRemoteViewsToApply(context, size); // In the case that a view has this RemoteViews applied in one orientation or size, is @@ -4942,7 +4975,7 @@ public class RemoteViews implements Parcelable, Filter { } } - rvToApply.performApply(v, (ViewGroup) v.getParent(), handler); + rvToApply.performApply(v, (ViewGroup) v.getParent(), handler, colorResources); } /** @@ -4958,20 +4991,21 @@ public class RemoteViews implements Parcelable, Filter { * @return CancellationSignal * @hide */ - public CancellationSignal reapplyAsync( - Context context, View v, Executor executor, OnViewAppliedListener listener) { - return reapplyAsync(context, v, executor, listener, null, null); + public CancellationSignal reapplyAsync(Context context, View v, Executor executor, + OnViewAppliedListener listener) { + return reapplyAsync(context, v, executor, listener, null); } /** @hide */ public CancellationSignal reapplyAsync(Context context, View v, Executor executor, OnViewAppliedListener listener, InteractionHandler handler) { - return reapplyAsync(context, v, executor, listener, handler, null); + return reapplyAsync(context, v, executor, listener, handler, null, null); } /** @hide */ public CancellationSignal reapplyAsync(Context context, View v, Executor executor, - OnViewAppliedListener listener, InteractionHandler handler, PointF size) { + OnViewAppliedListener listener, InteractionHandler handler, PointF size, + ColorResources colorResources) { RemoteViews rvToApply = getRemoteViewsToApply(context, size); // In the case that a view has this RemoteViews applied in one orientation, is persisted @@ -4985,16 +5019,18 @@ public class RemoteViews implements Parcelable, Filter { } return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(), - context, listener, handler, v).startTaskOnExecutor(executor); + context, listener, handler, colorResources, v).startTaskOnExecutor( + executor); } - private void performApply(View v, ViewGroup parent, InteractionHandler handler) { + private void performApply(View v, ViewGroup parent, InteractionHandler handler, + ColorResources colorResources) { if (mActions != null) { handler = handler == null ? DEFAULT_INTERACTION_HANDLER : handler; final int count = mActions.size(); for (int i = 0; i < count; i++) { Action a = mActions.get(i); - a.apply(v, parent, handler); + a.apply(v, parent, handler, colorResources); } } } @@ -5035,6 +5071,122 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Object allowing the modification of a context to overload the system's dynamic colors. + * + * Only colors from {@link android.R.color#system_primary_0} to + * {@link android.R.color#system_neutral_1000} can be overloaded. + * @hide + */ + public static final class ColorResources { + // Set of valid colors resources. + private static final int FIRST_RESOURCE_COLOR_ID = android.R.color.system_primary_0; + private static final int LAST_RESOURCE_COLOR_ID = android.R.color.system_neutral_1000; + // Size, in bytes, of an entry in the array of colors in an ARSC file. + private static final int ARSC_ENTRY_SIZE = 16; + + private ResourcesLoader mLoader; + + private ColorResources(ResourcesLoader loader) { + mLoader = loader; + } + + /** + * Apply the color resources to the given context. + * + * No resource resolution must have be done on the context given to that method. + */ + public void apply(Context context) { + context.getResources().addLoaders(mLoader); + } + + private static ByteArrayOutputStream readFileContent(InputStream input) throws IOException { + ByteArrayOutputStream content = new ByteArrayOutputStream(2048); + byte[] buffer = new byte[4096]; + while (input.available() > 0) { + int read = input.read(buffer); + content.write(buffer, 0, read); + } + return content; + } + + /** + * Creates the compiled resources content from the asset stored in the APK. + * + * The asset is a compiled resource with the correct resources name and correct ids, only + * the values are incorrect. The last value is at the very end of the file. The resources + * are in an array, the array's entries are 16 bytes each. We use this to work out the + * location of all the positions of the various resources. + */ + private static byte[] createCompiledResourcesContent(Context context, + SparseIntArray colorResources) throws IOException { + byte[] content; + try (InputStream input = context.getResources().openRawResource( + com.android.internal.R.raw.remote_views_color_resources)) { + ByteArrayOutputStream rawContent = readFileContent(input); + content = rawContent.toByteArray(); + } + int valuesOffset = + content.length - (LAST_RESOURCE_COLOR_ID & 0xffff) * ARSC_ENTRY_SIZE - 4; + if (valuesOffset < 0) { + Log.e(LOG_TAG, "ARSC file for theme colors is invalid."); + return null; + } + for (int colorRes = FIRST_RESOURCE_COLOR_ID; colorRes <= LAST_RESOURCE_COLOR_ID; + colorRes++) { + // The last 2 bytes are the index in the color array. + int index = colorRes & 0xffff; + int offset = valuesOffset + index * ARSC_ENTRY_SIZE; + int value = colorResources.get(colorRes, context.getColor(colorRes)); + // Write the 32 bit integer in little endian + for (int b = 0; b < 4; b++) { + content[offset + b] = (byte) (value & 0xff); + value >>= 8; + } + } + return content; + } + + /** + * Adds a resource loader for theme colors to the given context. + * + * @param context Context of the view hosting the widget. + * @param colorMapping Mapping of resources to color values. + * + * @hide + */ + public static ColorResources create(Context context, SparseIntArray colorMapping) { + try { + byte[] contentBytes = createCompiledResourcesContent(context, colorMapping); + if (contentBytes == null) { + return null; + } + FileDescriptor arscFile = null; + try { + arscFile = Os.memfd_create("remote_views_theme_colors.arsc", 0 /* flags */); + // Note: This must not be closed through the OutputStream. + try (OutputStream pipeWriter = new FileOutputStream(arscFile)) { + pipeWriter.write(contentBytes); + + try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(arscFile)) { + ResourcesLoader colorsLoader = new ResourcesLoader(); + colorsLoader.addProvider(ResourcesProvider + .loadFromTable(pfd, null /* assetsProvider */)); + return new ColorResources(colorsLoader); + } + } + } finally { + if (arscFile != null) { + Os.close(arscFile); + } + } + } catch (Exception ex) { + Log.e(LOG_TAG, "Failed to setup the context for theme colors", ex); + } + return null; + } + } + + /** * Returns the number of actions in this RemoteViews. Can be used as a sequence number. * * @hide diff --git a/core/java/android/widget/RemoteViewsListAdapter.java b/core/java/android/widget/RemoteViewsListAdapter.java index b80fe4871616..827d03317d6a 100644 --- a/core/java/android/widget/RemoteViewsListAdapter.java +++ b/core/java/android/widget/RemoteViewsListAdapter.java @@ -31,12 +31,14 @@ public class RemoteViewsListAdapter extends BaseAdapter { private ArrayList<RemoteViews> mRemoteViewsList; private ArrayList<Integer> mViewTypes = new ArrayList<Integer>(); private int mViewTypeCount; + private RemoteViews.ColorResources mColorResources; public RemoteViewsListAdapter(Context context, ArrayList<RemoteViews> remoteViews, - int viewTypeCount) { + int viewTypeCount, RemoteViews.ColorResources colorResources) { mContext = context; mRemoteViewsList = remoteViews; mViewTypeCount = viewTypeCount; + mColorResources = colorResources; init(); } @@ -90,9 +92,10 @@ public class RemoteViewsListAdapter extends BaseAdapter { if (convertView != null && rv != null && convertView.getId() == rv.getLayoutId()) { v = convertView; - rv.reapply(mContext, v); + rv.reapply(mContext, v, null /* handler */, null /* size */, mColorResources); } else { - v = rv.apply(mContext, parent); + v = rv.apply(mContext, parent, null /* handler */, null /* size */, + mColorResources); } return v; } else { diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index 64d09de49f2d..65f3da79afe0 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -335,6 +335,27 @@ public class ScrollView extends FrameLayout { } /** + * Returns the {@link EdgeEffect#getType()} for the edge effects. + * @return the {@link EdgeEffect#getType()} for the edge effects. + * @attr ref android.R.styleable#EdgeEffect_edgeEffectType + */ + @EdgeEffect.EdgeEffectType + public int getEdgeEffectType() { + return mEdgeGlowTop.getType(); + } + + /** + * Sets the {@link EdgeEffect#setType(int)} for the edge effects. + * @param type The edge effect type to use for the edge effects. + * @attr ref android.R.styleable#EdgeEffect_edgeEffectType + */ + public void setEdgeEffectType(@EdgeEffect.EdgeEffectType int type) { + mEdgeGlowTop.setType(type); + mEdgeGlowBottom.setType(type); + invalidate(); + } + + /** * @return The maximum amount this scroll view will scroll in response to * an arrow event. */ diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 0f2089a5463f..ca0747fadf14 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -4756,6 +4756,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @see #getJustificationMode() */ @Layout.JustificationMode + @android.view.RemotableViewMethod public void setJustificationMode(@Layout.JustificationMode int justificationMode) { mJustificationMode = justificationMode; if (mLayout != null) { @@ -5232,6 +5233,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @see android.view.Gravity * @attr ref android.R.styleable#TextView_gravity */ + @android.view.RemotableViewMethod public void setGravity(int gravity) { if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) { gravity |= Gravity.START; @@ -5826,6 +5828,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * * @attr ref android.R.styleable#TextView_lineHeight */ + @android.view.RemotableViewMethod public void setLineHeight(@Px @IntRange(from = 0) int lineHeight) { Preconditions.checkArgumentNonnegative(lineHeight); @@ -10277,6 +10280,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @see #setTransformationMethod(TransformationMethod) * @attr ref android.R.styleable#TextView_textAllCaps */ + @android.view.RemotableViewMethod public void setAllCaps(boolean allCaps) { if (allCaps) { setTransformationMethod(new AllCapsTransformationMethod(getContext())); diff --git a/core/java/android/widget/ToastPresenter.java b/core/java/android/widget/ToastPresenter.java index 2904a8c889a2..0f2a3ca6936b 100644 --- a/core/java/android/widget/ToastPresenter.java +++ b/core/java/android/widget/ToastPresenter.java @@ -184,7 +184,7 @@ public class ToastPresenter { mParams.y = yOffset; mParams.horizontalMargin = horizontalMargin; mParams.verticalMargin = verticalMargin; - addToastView(); + mView.setLayoutParams(mParams); } /** diff --git a/core/java/com/android/internal/graphics/palette/CelebiQuantizer.java b/core/java/com/android/internal/graphics/palette/CelebiQuantizer.java new file mode 100644 index 000000000000..de6bf2044dbc --- /dev/null +++ b/core/java/com/android/internal/graphics/palette/CelebiQuantizer.java @@ -0,0 +1,50 @@ +/* + * 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.internal.graphics.palette; + +import java.util.List; + +/** + * An implementation of Celebi's WSM quantizer, or, a Kmeans quantizer that starts with centroids + * from a Wu quantizer to ensure 100% reproducible and quality results, and has some optimizations + * to the Kmeans algorithm. + * + * See Celebi 2011, “Improving the Performance of K-Means for Color Quantization” + */ +public class CelebiQuantizer implements Quantizer { + private List<Palette.Swatch> mSwatches; + + public CelebiQuantizer() { } + + @Override + public void quantize(int[] pixels, int maxColors) { + WuQuantizer wu = new WuQuantizer(pixels, maxColors); + wu.quantize(pixels, maxColors); + List<Palette.Swatch> wuSwatches = wu.getQuantizedColors(); + LABCentroid labCentroidProvider = new LABCentroid(); + WSMeansQuantizer kmeans = + new WSMeansQuantizer(WSMeansQuantizer.createStartingCentroids(labCentroidProvider, + wuSwatches), labCentroidProvider, pixels, maxColors); + kmeans.quantize(pixels, maxColors); + mSwatches = kmeans.getQuantizedColors(); + } + + @Override + public List<Palette.Swatch> getQuantizedColors() { + return mSwatches; + } +} diff --git a/core/java/android/uwb/AngleOfArrivalSupport.aidl b/core/java/com/android/internal/graphics/palette/CentroidProvider.java index 57666ff8bca9..5fcfcbab3159 100644 --- a/core/java/android/uwb/AngleOfArrivalSupport.aidl +++ b/core/java/com/android/internal/graphics/palette/CentroidProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Android Open Source Project + * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,31 +14,25 @@ * limitations under the License. */ -package android.uwb; +package com.android.internal.graphics.palette; -/** - * @hide - */ -@Backing(type="int") -enum AngleOfArrivalSupport { - /** - * The device does not support angle of arrival - */ - NONE, +import android.annotation.ColorInt; - /** - * The device supports planar angle of arrival - */ - TWO_DIMENSIONAL, +interface CentroidProvider { + /** + * @return 3 dimensions representing the color + */ + float[] getCentroid(@ColorInt int color); - /** - * The device does supports three dimensional angle of arrival with hemispherical azimuth angles - */ - THREE_DIMENSIONAL_HEMISPHERICAL, + /** + * @param centroid 3 dimensions representing the color + * @return 32-bit ARGB representation + */ + @ColorInt + int getColor(float[] centroid); - /** - * The device does supports three dimensional angle of arrival with full azimuth angles - */ - THREE_DIMENSIONAL_SPHERICAL, + /** + * Distance between two centroids. + */ + float distance(float[] a, float[] b); } - diff --git a/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java b/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java index 9ac753b6d6ce..777949434c36 100644 --- a/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java +++ b/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java @@ -35,6 +35,8 @@ package com.android.internal.graphics.palette; import android.graphics.Color; import android.util.TimingLogger; +import com.android.internal.graphics.palette.Palette.Swatch; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -42,9 +44,6 @@ import java.util.Comparator; import java.util.List; import java.util.PriorityQueue; -import com.android.internal.graphics.ColorUtils; -import com.android.internal.graphics.palette.Palette.Swatch; - /** * Copied from: frameworks/support/v7/palette/src/main/java/android/support/v7/ * graphics/ColorCutQuantizer.java @@ -77,20 +76,17 @@ final class ColorCutQuantizer implements Quantizer { int[] mHistogram; List<Swatch> mQuantizedColors; TimingLogger mTimingLogger; - Palette.Filter[] mFilters; private final float[] mTempHsl = new float[3]; /** * Execute color quantization. * - * @param pixels histogram representing an image's pixel data + * @param pixels histogram representing an image's pixel data * @param maxColors The maximum number of colors that should be in the result palette. - * @param filters Set of filters to use in the quantization stage */ - public void quantize(final int[] pixels, final int maxColors, final Palette.Filter[] filters) { + public void quantize(final int[] pixels, final int maxColors) { mTimingLogger = LOG_TIMINGS ? new TimingLogger(LOG_TAG, "Creation") : null; - mFilters = filters; final int[] hist = mHistogram = new int[1 << (QUANTIZE_WORD_WIDTH * 3)]; for (int i = 0; i < pixels.length; i++) { @@ -108,10 +104,6 @@ final class ColorCutQuantizer implements Quantizer { // Now let's count the number of distinct colors int distinctColorCount = 0; for (int color = 0; color < hist.length; color++) { - if (hist[color] > 0 && shouldIgnoreColor(color)) { - // If we should ignore the color, set the population to 0 - hist[color] = 0; - } if (hist[color] > 0) { // If the color has population, increase the distinct color count distinctColorCount++; @@ -186,7 +178,7 @@ final class ColorCutQuantizer implements Quantizer { * and splitting them. Once split, the new box and the remaining box are offered back to the * queue. * - * @param queue {@link java.util.PriorityQueue} to poll for boxes + * @param queue {@link java.util.PriorityQueue} to poll for boxes * @param maxSize Maximum amount of boxes to split */ private void splitBoxes(final PriorityQueue<Vbox> queue, final int maxSize) { @@ -216,11 +208,7 @@ final class ColorCutQuantizer implements Quantizer { ArrayList<Swatch> colors = new ArrayList<>(vboxes.size()); for (Vbox vbox : vboxes) { Swatch swatch = vbox.getAverageColor(); - if (!shouldIgnoreColor(swatch)) { - // As we're averaging a color box, we can still get colors which we do not want, so - // we check again here - colors.add(swatch); - } + colors.add(swatch); } return colors; } @@ -230,7 +218,7 @@ final class ColorCutQuantizer implements Quantizer { */ private class Vbox { // lower and upper index are inclusive - private int mLowerIndex; + private final int mLowerIndex; private int mUpperIndex; // Population of colors within this box private int mPopulation; @@ -373,7 +361,7 @@ final class ColorCutQuantizer implements Quantizer { modifySignificantOctet(colors, longestDimension, mLowerIndex, mUpperIndex); final int midPoint = mPopulation / 2; - for (int i = mLowerIndex, count = 0; i <= mUpperIndex; i++) { + for (int i = mLowerIndex, count = 0; i <= mUpperIndex; i++) { count += hist[colors[i]]; if (count >= midPoint) { // we never want to split on the upperIndex, as this will result in the same @@ -447,27 +435,6 @@ final class ColorCutQuantizer implements Quantizer { } } - private boolean shouldIgnoreColor(int color565) { - final int rgb = approximateToRgb888(color565); - ColorUtils.colorToHSL(rgb, mTempHsl); - return shouldIgnoreColor(rgb, mTempHsl); - } - - private boolean shouldIgnoreColor(Swatch color) { - return shouldIgnoreColor(color.getRgb(), color.getHsl()); - } - - private boolean shouldIgnoreColor(int rgb, float[] hsl) { - if (mFilters != null && mFilters.length > 0) { - for (int i = 0, count = mFilters.length; i < count; i++) { - if (!mFilters[i].isAllowed(rgb, hsl)) { - return true; - } - } - } - return false; - } - /** * Comparator which sorts {@link Vbox} instances based on their volume, in descending order */ @@ -498,7 +465,8 @@ final class ColorCutQuantizer implements Quantizer { } private static int approximateToRgb888(int color) { - return approximateToRgb888(quantizedRed(color), quantizedGreen(color), quantizedBlue(color)); + return approximateToRgb888(quantizedRed(color), quantizedGreen(color), + quantizedBlue(color)); } /** diff --git a/core/java/com/android/internal/graphics/palette/Contrast.java b/core/java/com/android/internal/graphics/palette/Contrast.java new file mode 100644 index 000000000000..3dd1b8d8117c --- /dev/null +++ b/core/java/com/android/internal/graphics/palette/Contrast.java @@ -0,0 +1,103 @@ +/* + * 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.internal.graphics.palette; + +/** + * Helper methods for determining contrast between two colors, either via the colors themselves + * or components in different color spaces. + */ +public class Contrast { + /** + * + * @param y Y in XYZ that contrasts with the returned Y value + * @param contrast contrast ratio between color argument and returned Y value. Must be >= 1 + * or an exception will be thrown + * @return the lower Y coordinate in XYZ space that contrasts with color, or -1 if reaching + * no Y coordinate reaches contrast with color. + */ + public static float lighterY(float y, float contrast) { + assert (contrast >= 1); + float answer = -5 + contrast * (5 + y); + if (answer > 100.0) { + return -1; + } + return answer; + } + + + /** + * @param y Y in XYZ that contrasts with the returned Y value + * @param contrast contrast ratio between color argument and returned Y value. Must be >= 1 + * or an exception will be thrown + * @return the lower Y coordinate in XYZ space that contrasts with color, or -1 if reaching + * no Y coordinate reaches contrast with color. + */ + public static float darkerY(float y, float contrast) { + assert (contrast >= 1); + float answer = (5 - 5 * contrast + y) / contrast; + if (answer < 0.0) { + return -1; + } + return answer; + } + + /** + * Convert L* in L*a*b* to Y in XYZ. + * + * @param lstar L* in L*a*b* + * @return Y in XYZ + */ + public static float lstarToY(float lstar) { + // http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html + float ke = 8.0f; + if (lstar > ke) { + return (float) (Math.pow(((lstar + 16.0) / 116.0), 3) * 100.0); + } else { + return (float) (lstar / (24389 / 27) * 100.0); + } + } + + /** + * Convert Y in XYZ to L* in L*a*b*. + * + * @param y Y in XYZ + * @return L* in L*a*b* + */ + public static float yToLstar(float y) { + y = y / 100.0f; + float e = 216.0f / 24389.0f; + float y_intermediate; + if (y <= e) { + y_intermediate = (24389.f / 27.f) * y; + // If y < e, can skip consecutive steps of / 116 + 16 followed by * 116 - 16. + return y_intermediate; + } else { + y_intermediate = (float) Math.cbrt(y); + } + return 116.f * y_intermediate - 16.f; + } + + + /** + * @return Contrast ratio between two Y values in XYZ space. + */ + public static float contrastYs(float y1, float y2) { + final float lighter = Math.max(y1, y2); + final float darker = (lighter == y1) ? y2 : y1; + return (lighter + 5) / (darker + 5); + } +} diff --git a/core/java/com/android/internal/graphics/palette/LABCentroid.java b/core/java/com/android/internal/graphics/palette/LABCentroid.java new file mode 100644 index 000000000000..98d5d2684857 --- /dev/null +++ b/core/java/com/android/internal/graphics/palette/LABCentroid.java @@ -0,0 +1,67 @@ +/* + * 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.internal.graphics.palette; + +import android.graphics.Color; +import android.graphics.ColorSpace; + +/** + * Allows quantizers to operate in the L*a*b* colorspace. + * L*a*b* is a good choice for measuring distance between colors. + * Better spaces, and better distance calculations even in L*a*b* exist, but measuring distance + * in L*a*b* space, also known as deltaE, is a universally accepted standard across industries + * and worldwide. + */ +public class LABCentroid implements CentroidProvider { + final ColorSpace.Connector mRgbToLab; + final ColorSpace.Connector mLabToRgb; + + public LABCentroid() { + mRgbToLab = ColorSpace.connect( + ColorSpace.get(ColorSpace.Named.SRGB), + ColorSpace.get(ColorSpace.Named.CIE_LAB)); + mLabToRgb = ColorSpace.connect(ColorSpace.get(ColorSpace.Named.CIE_LAB), + ColorSpace.get(ColorSpace.Named.SRGB)); + } + + @Override + public float[] getCentroid(int color) { + float r = Color.red(color) / 255.f; + float g = Color.green(color) / 255.f; + float b = Color.blue(color) / 255.f; + + float[] transform = mRgbToLab.transform(r, g, b); + return transform; + } + + @Override + public int getColor(float[] centroid) { + float[] rgb = mLabToRgb.transform(centroid); + int color = Color.rgb(rgb[0], rgb[1], rgb[2]); + return color; + } + + @Override + public float distance(float[] a, float[] b) { + // Standard v1 CIELAB deltaE formula, 1976 - easily improved upon, however, + // improvements do not significantly impact the Palette algorithm's results. + double dL = a[0] - b[0]; + double dA = a[1] - b[1]; + double dB = a[2] - b[2]; + return (float) (Math.pow(dL, 2) + Math.pow(dA, 2) + Math.pow(dB, 2)); + } +} diff --git a/core/java/com/android/internal/graphics/palette/Mean.java b/core/java/com/android/internal/graphics/palette/Mean.java new file mode 100644 index 000000000000..894f91b6261c --- /dev/null +++ b/core/java/com/android/internal/graphics/palette/Mean.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.internal.graphics.palette; + +import java.util.Random; + +/** + * Represents a centroid in Kmeans algorithms. + */ +public class Mean { + private static final Random RANDOM = new Random(0); + + public float[] center; + + /** + * Constructor. + * + * @param upperBound maximum value of a dimension in the space Kmeans is optimizing in + */ + Mean(int upperBound) { + center = + new float[]{ + RANDOM.nextInt(upperBound + 1), RANDOM.nextInt(upperBound + 1), + RANDOM.nextInt(upperBound + 1) + }; + } + + Mean(float[] center) { + this.center = center; + } +} diff --git a/core/java/com/android/internal/graphics/palette/MeanBucket.java b/core/java/com/android/internal/graphics/palette/MeanBucket.java new file mode 100644 index 000000000000..ae8858a8107c --- /dev/null +++ b/core/java/com/android/internal/graphics/palette/MeanBucket.java @@ -0,0 +1,42 @@ +/* + * 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.internal.graphics.palette; + +import java.util.HashSet; +import java.util.Set; + +class MeanBucket { + float[] mTotal = {0.f, 0.f, 0.f}; + int mCount = 0; + Set<Integer> mColors = new HashSet<>(); + + void add(float[] colorAsDoubles, int color, int colorCount) { + assert (colorAsDoubles.length == 3); + mColors.add(color); + mTotal[0] += (colorAsDoubles[0] * colorCount); + mTotal[1] += (colorAsDoubles[1] * colorCount); + mTotal[2] += (colorAsDoubles[2] * colorCount); + mCount += colorCount; + } + + float[] getCentroid() { + if (mCount == 0) { + return null; + } + return new float[]{mTotal[0] / mCount, mTotal[1] / mCount, mTotal[2] / mCount}; + } +} diff --git a/core/java/com/android/internal/graphics/palette/Palette.java b/core/java/com/android/internal/graphics/palette/Palette.java index a4f9a596050c..8b1137d7de7c 100644 --- a/core/java/com/android/internal/graphics/palette/Palette.java +++ b/core/java/com/android/internal/graphics/palette/Palette.java @@ -19,48 +19,24 @@ package com.android.internal.graphics.palette; import android.annotation.ColorInt; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.Px; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Rect; -import android.os.AsyncTask; -import android.util.ArrayMap; import android.util.Log; -import android.util.SparseBooleanArray; -import android.util.TimingLogger; -import com.android.internal.graphics.ColorUtils; - -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Map; /** - * Copied from: /frameworks/support/v7/palette/src/main/java/android/support/v7/ - * graphics/Palette.java - * * A helper class to extract prominent colors from an image. - * <p> - * A number of colors with different profiles are extracted from the image: - * <ul> - * <li>Vibrant</li> - * <li>Vibrant Dark</li> - * <li>Vibrant Light</li> - * <li>Muted</li> - * <li>Muted Dark</li> - * <li>Muted Light</li> - * </ul> - * These can be retrieved from the appropriate getter method. * - * <p> - * Instances are created with a {@link Palette.Builder} which supports several options to tweak the + * <p>Instances are created with a {@link Builder} which supports several options to tweak the * generated Palette. See that class' documentation for more information. - * <p> - * Generation should always be completed on a background thread, ideally the one in - * which you load your image on. {@link Palette.Builder} supports both synchronous and asynchronous - * generation: + * + * <p>Generation should always be completed on a background thread, ideally the one in which you + * load your image on. {@link Builder} supports both synchronous and asynchronous generation: * * <pre> * // Synchronous @@ -85,346 +61,59 @@ public final class Palette { /** * Called when the {@link Palette} has been generated. */ - void onGenerated(Palette palette); + void onGenerated(@Nullable Palette palette); } static final int DEFAULT_RESIZE_BITMAP_AREA = 112 * 112; static final int DEFAULT_CALCULATE_NUMBER_COLORS = 16; - - static final float MIN_CONTRAST_TITLE_TEXT = 3.0f; - static final float MIN_CONTRAST_BODY_TEXT = 4.5f; - static final String LOG_TAG = "Palette"; - static final boolean LOG_TIMINGS = false; - /** - * Start generating a {@link Palette} with the returned {@link Palette.Builder} instance. - */ - public static Palette.Builder from(Bitmap bitmap) { - return new Palette.Builder(bitmap); + /** Start generating a {@link Palette} with the returned {@link Builder} instance. */ + @NonNull + public static Builder from(@NonNull Bitmap bitmap, @NonNull Quantizer quantizer) { + return new Builder(bitmap, quantizer); } /** * Generate a {@link Palette} from the pre-generated list of {@link Palette.Swatch} swatches. - * This is useful for testing, or if you want to resurrect a {@link Palette} instance from a - * list of swatches. Will return null if the {@code swatches} is null. - */ - public static Palette from(List<Palette.Swatch> swatches) { - return new Palette.Builder(swatches).generate(); - } - - /** - * @deprecated Use {@link Palette.Builder} to generate the Palette. - */ - @Deprecated - public static Palette generate(Bitmap bitmap) { - return from(bitmap).generate(); - } - - /** - * @deprecated Use {@link Palette.Builder} to generate the Palette. - */ - @Deprecated - public static Palette generate(Bitmap bitmap, int numColors) { - return from(bitmap).maximumColorCount(numColors).generate(); - } - - /** - * @deprecated Use {@link Palette.Builder} to generate the Palette. + * This + * is useful for testing, or if you want to resurrect a {@link Palette} instance from a list of + * swatches. Will return null if the {@code swatches} is null. */ - @Deprecated - public static AsyncTask<Bitmap, Void, Palette> generateAsync( - Bitmap bitmap, Palette.PaletteAsyncListener listener) { - return from(bitmap).generate(listener); - } - - /** - * @deprecated Use {@link Palette.Builder} to generate the Palette. - */ - @Deprecated - public static AsyncTask<Bitmap, Void, Palette> generateAsync( - final Bitmap bitmap, final int numColors, final Palette.PaletteAsyncListener listener) { - return from(bitmap).maximumColorCount(numColors).generate(listener); + @NonNull + public static Palette from(@NonNull List<Swatch> swatches) { + return new Builder(swatches).generate(); } - private final List<Palette.Swatch> mSwatches; - private final List<Target> mTargets; + private final List<Swatch> mSwatches; - private final Map<Target, Palette.Swatch> mSelectedSwatches; - private final SparseBooleanArray mUsedColors; - private final Palette.Swatch mDominantSwatch; + @Nullable + private final Swatch mDominantSwatch; - Palette(List<Palette.Swatch> swatches, List<Target> targets) { + Palette(List<Swatch> swatches) { mSwatches = swatches; - mTargets = targets; - - mUsedColors = new SparseBooleanArray(); - mSelectedSwatches = new ArrayMap<>(); - mDominantSwatch = findDominantSwatch(); } - /** - * Returns all of the swatches which make up the palette. - */ + /** Returns all of the swatches which make up the palette. */ @NonNull - public List<Palette.Swatch> getSwatches() { + public List<Swatch> getSwatches() { return Collections.unmodifiableList(mSwatches); } - /** - * Returns the targets used to generate this palette. - */ - @NonNull - public List<Target> getTargets() { - return Collections.unmodifiableList(mTargets); - } - - /** - * Returns the most vibrant swatch in the palette. Might be null. - * - * @see Target#VIBRANT - */ - @Nullable - public Palette.Swatch getVibrantSwatch() { - return getSwatchForTarget(Target.VIBRANT); - } - - /** - * Returns a light and vibrant swatch from the palette. Might be null. - * - * @see Target#LIGHT_VIBRANT - */ - @Nullable - public Palette.Swatch getLightVibrantSwatch() { - return getSwatchForTarget(Target.LIGHT_VIBRANT); - } - - /** - * Returns a dark and vibrant swatch from the palette. Might be null. - * - * @see Target#DARK_VIBRANT - */ - @Nullable - public Palette.Swatch getDarkVibrantSwatch() { - return getSwatchForTarget(Target.DARK_VIBRANT); - } - - /** - * Returns a muted swatch from the palette. Might be null. - * - * @see Target#MUTED - */ - @Nullable - public Palette.Swatch getMutedSwatch() { - return getSwatchForTarget(Target.MUTED); - } - - /** - * Returns a muted and light swatch from the palette. Might be null. - * - * @see Target#LIGHT_MUTED - */ - @Nullable - public Palette.Swatch getLightMutedSwatch() { - return getSwatchForTarget(Target.LIGHT_MUTED); - } - - /** - * Returns a muted and dark swatch from the palette. Might be null. - * - * @see Target#DARK_MUTED - */ + /** Returns the swatch with the highest population, or null if there are no swatches. */ @Nullable - public Palette.Swatch getDarkMutedSwatch() { - return getSwatchForTarget(Target.DARK_MUTED); - } - - /** - * Returns the most vibrant color in the palette as an RGB packed int. - * - * @param defaultColor value to return if the swatch isn't available - * @see #getVibrantSwatch() - */ - @ColorInt - public int getVibrantColor(@ColorInt final int defaultColor) { - return getColorForTarget(Target.VIBRANT, defaultColor); - } - - /** - * Returns a light and vibrant color from the palette as an RGB packed int. - * - * @param defaultColor value to return if the swatch isn't available - * @see #getLightVibrantSwatch() - */ - @ColorInt - public int getLightVibrantColor(@ColorInt final int defaultColor) { - return getColorForTarget(Target.LIGHT_VIBRANT, defaultColor); - } - - /** - * Returns a dark and vibrant color from the palette as an RGB packed int. - * - * @param defaultColor value to return if the swatch isn't available - * @see #getDarkVibrantSwatch() - */ - @ColorInt - public int getDarkVibrantColor(@ColorInt final int defaultColor) { - return getColorForTarget(Target.DARK_VIBRANT, defaultColor); - } - - /** - * Returns a muted color from the palette as an RGB packed int. - * - * @param defaultColor value to return if the swatch isn't available - * @see #getMutedSwatch() - */ - @ColorInt - public int getMutedColor(@ColorInt final int defaultColor) { - return getColorForTarget(Target.MUTED, defaultColor); - } - - /** - * Returns a muted and light color from the palette as an RGB packed int. - * - * @param defaultColor value to return if the swatch isn't available - * @see #getLightMutedSwatch() - */ - @ColorInt - public int getLightMutedColor(@ColorInt final int defaultColor) { - return getColorForTarget(Target.LIGHT_MUTED, defaultColor); - } - - /** - * Returns a muted and dark color from the palette as an RGB packed int. - * - * @param defaultColor value to return if the swatch isn't available - * @see #getDarkMutedSwatch() - */ - @ColorInt - public int getDarkMutedColor(@ColorInt final int defaultColor) { - return getColorForTarget(Target.DARK_MUTED, defaultColor); - } - - /** - * Returns the selected swatch for the given target from the palette, or {@code null} if one - * could not be found. - */ - @Nullable - public Palette.Swatch getSwatchForTarget(@NonNull final Target target) { - return mSelectedSwatches.get(target); - } - - /** - * Returns the selected color for the given target from the palette as an RGB packed int. - * - * @param defaultColor value to return if the swatch isn't available - */ - @ColorInt - public int getColorForTarget(@NonNull final Target target, @ColorInt final int defaultColor) { - Palette.Swatch swatch = getSwatchForTarget(target); - return swatch != null ? swatch.getRgb() : defaultColor; - } - - /** - * Returns the dominant swatch from the palette. - * - * <p>The dominant swatch is defined as the swatch with the greatest population (frequency) - * within the palette.</p> - */ - @Nullable - public Palette.Swatch getDominantSwatch() { + public Swatch getDominantSwatch() { return mDominantSwatch; } - /** - * Returns the color of the dominant swatch from the palette, as an RGB packed int. - * - * @param defaultColor value to return if the swatch isn't available - * @see #getDominantSwatch() - */ - @ColorInt - public int getDominantColor(@ColorInt int defaultColor) { - return mDominantSwatch != null ? mDominantSwatch.getRgb() : defaultColor; - } - - void generate() { - // We need to make sure that the scored targets are generated first. This is so that - // inherited targets have something to inherit from - for (int i = 0, count = mTargets.size(); i < count; i++) { - final Target target = mTargets.get(i); - target.normalizeWeights(); - mSelectedSwatches.put(target, generateScoredTarget(target)); - } - // We now clear out the used colors - mUsedColors.clear(); - } - - private Palette.Swatch generateScoredTarget(final Target target) { - final Palette.Swatch maxScoreSwatch = getMaxScoredSwatchForTarget(target); - if (maxScoreSwatch != null && target.isExclusive()) { - // If we have a swatch, and the target is exclusive, add the color to the used list - mUsedColors.append(maxScoreSwatch.getRgb(), true); - } - return maxScoreSwatch; - } - - private Palette.Swatch getMaxScoredSwatchForTarget(final Target target) { - float maxScore = 0; - Palette.Swatch maxScoreSwatch = null; - for (int i = 0, count = mSwatches.size(); i < count; i++) { - final Palette.Swatch swatch = mSwatches.get(i); - if (shouldBeScoredForTarget(swatch, target)) { - final float score = generateScore(swatch, target); - if (maxScoreSwatch == null || score > maxScore) { - maxScoreSwatch = swatch; - maxScore = score; - } - } - } - return maxScoreSwatch; - } - - private boolean shouldBeScoredForTarget(final Palette.Swatch swatch, final Target target) { - // Check whether the HSL values are within the correct ranges, and this color hasn't - // been used yet. - final float hsl[] = swatch.getHsl(); - return hsl[1] >= target.getMinimumSaturation() && hsl[1] <= target.getMaximumSaturation() - && hsl[2] >= target.getMinimumLightness() && hsl[2] <= target.getMaximumLightness() - && !mUsedColors.get(swatch.getRgb()); - } - - private float generateScore(Palette.Swatch swatch, Target target) { - final float[] hsl = swatch.getHsl(); - - float saturationScore = 0; - float luminanceScore = 0; - float populationScore = 0; - - final int maxPopulation = mDominantSwatch != null ? mDominantSwatch.getPopulation() : 1; - - if (target.getSaturationWeight() > 0) { - saturationScore = target.getSaturationWeight() - * (1f - Math.abs(hsl[1] - target.getTargetSaturation())); - } - if (target.getLightnessWeight() > 0) { - luminanceScore = target.getLightnessWeight() - * (1f - Math.abs(hsl[2] - target.getTargetLightness())); - } - if (target.getPopulationWeight() > 0) { - populationScore = target.getPopulationWeight() - * (swatch.getPopulation() / (float) maxPopulation); - } - - return saturationScore + luminanceScore + populationScore; - } - - private Palette.Swatch findDominantSwatch() { + @Nullable + private Swatch findDominantSwatch() { int maxPop = Integer.MIN_VALUE; - Palette.Swatch maxSwatch = null; + Swatch maxSwatch = null; for (int i = 0, count = mSwatches.size(); i < count; i++) { - Palette.Swatch swatch = mSwatches.get(i); + Swatch swatch = mSwatches.get(i); if (swatch.getPopulation() > maxPop) { maxSwatch = swatch; maxPop = swatch.getPopulation(); @@ -433,148 +122,42 @@ public final class Palette { return maxSwatch; } - private static float[] copyHslValues(Palette.Swatch color) { - final float[] newHsl = new float[3]; - System.arraycopy(color.getHsl(), 0, newHsl, 0, 3); - return newHsl; - } - /** * Represents a color swatch generated from an image's palette. The RGB color can be retrieved - * by calling {@link #getRgb()}. + * by + * calling {@link #getInt()}. */ - public static final class Swatch { - private final int mRed, mGreen, mBlue; - private final int mRgb; + public static class Swatch { + private final Color mColor; private final int mPopulation; - private boolean mGeneratedTextColors; - private int mTitleTextColor; - private int mBodyTextColor; - - private float[] mHsl; - - public Swatch(@ColorInt int color, int population) { - mRed = Color.red(color); - mGreen = Color.green(color); - mBlue = Color.blue(color); - mRgb = color; - mPopulation = population; - } - Swatch(int red, int green, int blue, int population) { - mRed = red; - mGreen = green; - mBlue = blue; - mRgb = Color.rgb(red, green, blue); + public Swatch(@ColorInt int colorInt, int population) { + mColor = Color.valueOf(colorInt); mPopulation = population; } - Swatch(float[] hsl, int population) { - this(ColorUtils.HSLToColor(hsl), population); - mHsl = hsl; - } - - /** - * @return this swatch's RGB color value - */ + /** @return this swatch's RGB color value */ @ColorInt - public int getRgb() { - return mRgb; + public int getInt() { + return mColor.toArgb(); } - /** - * Return this swatch's HSL values. - * hsv[0] is Hue [0 .. 360) - * hsv[1] is Saturation [0...1] - * hsv[2] is Lightness [0...1] - */ - public float[] getHsl() { - if (mHsl == null) { - mHsl = new float[3]; - } - ColorUtils.RGBToHSL(mRed, mGreen, mBlue, mHsl); - return mHsl; - } - - /** - * @return the number of pixels represented by this swatch - */ + /** @return the number of pixels represented by this swatch */ public int getPopulation() { return mPopulation; } - /** - * Returns an appropriate color to use for any 'title' text which is displayed over this - * {@link Palette.Swatch}'s color. This color is guaranteed to have sufficient contrast. - */ - @ColorInt - public int getTitleTextColor() { - ensureTextColorsGenerated(); - return mTitleTextColor; - } - - /** - * Returns an appropriate color to use for any 'body' text which is displayed over this - * {@link Palette.Swatch}'s color. This color is guaranteed to have sufficient contrast. - */ - @ColorInt - public int getBodyTextColor() { - ensureTextColorsGenerated(); - return mBodyTextColor; - } - - private void ensureTextColorsGenerated() { - if (!mGeneratedTextColors) { - // First check white, as most colors will be dark - final int lightBodyAlpha = ColorUtils.calculateMinimumAlpha( - Color.WHITE, mRgb, MIN_CONTRAST_BODY_TEXT); - final int lightTitleAlpha = ColorUtils.calculateMinimumAlpha( - Color.WHITE, mRgb, MIN_CONTRAST_TITLE_TEXT); - - if (lightBodyAlpha != -1 && lightTitleAlpha != -1) { - // If we found valid light values, use them and return - mBodyTextColor = ColorUtils.setAlphaComponent(Color.WHITE, lightBodyAlpha); - mTitleTextColor = ColorUtils.setAlphaComponent(Color.WHITE, lightTitleAlpha); - mGeneratedTextColors = true; - return; - } - - final int darkBodyAlpha = ColorUtils.calculateMinimumAlpha( - Color.BLACK, mRgb, MIN_CONTRAST_BODY_TEXT); - final int darkTitleAlpha = ColorUtils.calculateMinimumAlpha( - Color.BLACK, mRgb, MIN_CONTRAST_TITLE_TEXT); - - if (darkBodyAlpha != -1 && darkTitleAlpha != -1) { - // If we found valid dark values, use them and return - mBodyTextColor = ColorUtils.setAlphaComponent(Color.BLACK, darkBodyAlpha); - mTitleTextColor = ColorUtils.setAlphaComponent(Color.BLACK, darkTitleAlpha); - mGeneratedTextColors = true; - return; - } - - // If we reach here then we can not find title and body values which use the same - // lightness, we need to use mismatched values - mBodyTextColor = lightBodyAlpha != -1 - ? ColorUtils.setAlphaComponent(Color.WHITE, lightBodyAlpha) - : ColorUtils.setAlphaComponent(Color.BLACK, darkBodyAlpha); - mTitleTextColor = lightTitleAlpha != -1 - ? ColorUtils.setAlphaComponent(Color.WHITE, lightTitleAlpha) - : ColorUtils.setAlphaComponent(Color.BLACK, darkTitleAlpha); - mGeneratedTextColors = true; - } - } - @Override public String toString() { return new StringBuilder(getClass().getSimpleName()) - .append(" [RGB: #").append(Integer.toHexString(getRgb())).append(']') - .append(" [HSL: ").append(Arrays.toString(getHsl())).append(']') - .append(" [Population: ").append(mPopulation).append(']') - .append(" [Title Text: #").append(Integer.toHexString(getTitleTextColor())) + .append(" [") + .append(mColor) + .append(']') + .append(" [Population: ") + .append(mPopulation) .append(']') - .append(" [Body Text: #").append(Integer.toHexString(getBodyTextColor())) - .append(']').toString(); + .toString(); } @Override @@ -586,243 +169,168 @@ public final class Palette { return false; } - Palette.Swatch - swatch = (Palette.Swatch) o; - return mPopulation == swatch.mPopulation && mRgb == swatch.mRgb; + Swatch swatch = (Swatch) o; + return mPopulation == swatch.mPopulation && mColor.toArgb() == swatch.mColor.toArgb(); } @Override public int hashCode() { - return 31 * mRgb + mPopulation; + return 31 * mColor.toArgb() + mPopulation; } } - /** - * Builder class for generating {@link Palette} instances. - */ - public static final class Builder { - private final List<Palette.Swatch> mSwatches; + /** Builder class for generating {@link Palette} instances. */ + public static class Builder { + @Nullable + private final List<Swatch> mSwatches; + @Nullable private final Bitmap mBitmap; + @Nullable + private Quantizer mQuantizer = new ColorCutQuantizer(); - private final List<Target> mTargets = new ArrayList<>(); private int mMaxColors = DEFAULT_CALCULATE_NUMBER_COLORS; private int mResizeArea = DEFAULT_RESIZE_BITMAP_AREA; private int mResizeMaxDimension = -1; - private final List<Palette.Filter> mFilters = new ArrayList<>(); + @Nullable private Rect mRegion; - private Quantizer mQuantizer; - - /** - * Construct a new {@link Palette.Builder} using a source {@link Bitmap} - */ - public Builder(Bitmap bitmap) { + /** Construct a new {@link Builder} using a source {@link Bitmap} */ + public Builder(@NonNull Bitmap bitmap, @NonNull Quantizer quantizer) { if (bitmap == null || bitmap.isRecycled()) { throw new IllegalArgumentException("Bitmap is not valid"); } - mFilters.add(DEFAULT_FILTER); - mBitmap = bitmap; mSwatches = null; - - // Add the default targets - mTargets.add(Target.LIGHT_VIBRANT); - mTargets.add(Target.VIBRANT); - mTargets.add(Target.DARK_VIBRANT); - mTargets.add(Target.LIGHT_MUTED); - mTargets.add(Target.MUTED); - mTargets.add(Target.DARK_MUTED); + mBitmap = bitmap; + mQuantizer = quantizer == null ? new ColorCutQuantizer() : quantizer; } /** - * Construct a new {@link Palette.Builder} using a list of {@link Palette.Swatch} instances. - * Typically only used for testing. + * Construct a new {@link Builder} using a list of {@link Swatch} instances. Typically only + * used + * for testing. */ - public Builder(List<Palette.Swatch> swatches) { + public Builder(@NonNull List<Swatch> swatches) { if (swatches == null || swatches.isEmpty()) { throw new IllegalArgumentException("List of Swatches is not valid"); } - mFilters.add(DEFAULT_FILTER); mSwatches = swatches; mBitmap = null; + mQuantizer = null; } /** - * Set the maximum number of colors to use in the quantization step when using a - * {@link android.graphics.Bitmap} as the source. - * <p> - * Good values for depend on the source image type. For landscapes, good values are in - * the range 10-16. For images which are largely made up of people's faces then this - * value should be increased to ~24. + * Set the maximum number of colors to use in the quantization step when using a {@link + * android.graphics.Bitmap} as the source. + * + * <p>Good values for depend on the source image type. For landscapes, good values are in + * the + * range 10-16. For images which are largely made up of people's faces then this value + * should be + * increased to ~24. */ @NonNull - public Palette.Builder maximumColorCount(int colors) { + public Builder maximumColorCount(int colors) { mMaxColors = colors; return this; } /** - * Set the resize value when using a {@link android.graphics.Bitmap} as the source. - * If the bitmap's largest dimension is greater than the value specified, then the bitmap - * will be resized so that its largest dimension matches {@code maxDimension}. If the - * bitmap is smaller or equal, the original is used as-is. - * - * @deprecated Using {@link #resizeBitmapArea(int)} is preferred since it can handle - * abnormal aspect ratios more gracefully. + * Set the resize value when using a {@link android.graphics.Bitmap} as the source. If the + * bitmap's largest dimension is greater than the value specified, then the bitmap will be + * resized so that its largest dimension matches {@code maxDimension}. If the bitmap is + * smaller + * or equal, the original is used as-is. * * @param maxDimension the number of pixels that the max dimension should be scaled down to, - * or any value <= 0 to disable resizing. + * or + * any value <= 0 to disable resizing. + * @deprecated Using {@link #resizeBitmapArea(int)} is preferred since it can handle + * abnormal + * aspect ratios more gracefully. */ @NonNull @Deprecated - public Palette.Builder resizeBitmapSize(final int maxDimension) { + public Builder resizeBitmapSize(int maxDimension) { mResizeMaxDimension = maxDimension; mResizeArea = -1; return this; } /** - * Set the resize value when using a {@link android.graphics.Bitmap} as the source. - * If the bitmap's area is greater than the value specified, then the bitmap - * will be resized so that its area matches {@code area}. If the - * bitmap is smaller or equal, the original is used as-is. - * <p> - * This value has a large effect on the processing time. The larger the resized image is, - * the greater time it will take to generate the palette. The smaller the image is, the - * more detail is lost in the resulting image and thus less precision for color selection. + * Set the resize value when using a {@link android.graphics.Bitmap} as the source. If the + * bitmap's area is greater than the value specified, then the bitmap will be resized so + * that + * its area matches {@code area}. If the bitmap is smaller or equal, the original is used + * as-is. + * + * <p>This value has a large effect on the processing time. The larger the resized image is, + * the + * greater time it will take to generate the palette. The smaller the image is, the more + * detail + * is lost in the resulting image and thus less precision for color selection. * * @param area the number of pixels that the intermediary scaled down Bitmap should cover, - * or any value <= 0 to disable resizing. + * or + * any value <= 0 to disable resizing. */ @NonNull - public Palette.Builder resizeBitmapArea(final int area) { + public Builder resizeBitmapArea(int area) { mResizeArea = area; mResizeMaxDimension = -1; return this; } /** - * Clear all added filters. This includes any default filters added automatically by - * {@link Palette}. - */ - @NonNull - public Palette.Builder clearFilters() { - mFilters.clear(); - return this; - } - - /** - * Add a filter to be able to have fine grained control over which colors are - * allowed in the resulting palette. - * - * @param filter filter to add. - */ - @NonNull - public Palette.Builder addFilter( - Palette.Filter filter) { - if (filter != null) { - mFilters.add(filter); - } - return this; - } - - /** - * Set a specific quantization algorithm. {@link ColorCutQuantizer} will - * be used if unspecified. - * - * @param quantizer Quantizer implementation. - */ - @NonNull - public Palette.Builder setQuantizer(Quantizer quantizer) { - mQuantizer = quantizer; - return this; - } - - /** * Set a region of the bitmap to be used exclusively when calculating the palette. - * <p>This only works when the original input is a {@link Bitmap}.</p> * - * @param left The left side of the rectangle used for the region. - * @param top The top of the rectangle used for the region. - * @param right The right side of the rectangle used for the region. + * <p>This only works when the original input is a {@link Bitmap}. + * + * @param left The left side of the rectangle used for the region. + * @param top The top of the rectangle used for the region. + * @param right The right side of the rectangle used for the region. * @param bottom The bottom of the rectangle used for the region. */ @NonNull - public Palette.Builder setRegion(int left, int top, int right, int bottom) { + public Builder setRegion(@Px int left, @Px int top, @Px int right, @Px int bottom) { if (mBitmap != null) { if (mRegion == null) mRegion = new Rect(); // Set the Rect to be initially the whole Bitmap mRegion.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); // Now just get the intersection with the region if (!mRegion.intersect(left, top, right, bottom)) { - throw new IllegalArgumentException("The given region must intersect with " - + "the Bitmap's dimensions."); + throw new IllegalArgumentException( + "The given region must intersect with " + "the Bitmap's dimensions."); } } return this; } - /** - * Clear any previously region set via {@link #setRegion(int, int, int, int)}. - */ + /** Clear any previously region set via {@link #setRegion(int, int, int, int)}. */ @NonNull - public Palette.Builder clearRegion() { + public Builder clearRegion() { mRegion = null; return this; } - /** - * Add a target profile to be generated in the palette. - * - * <p>You can retrieve the result via {@link Palette#getSwatchForTarget(Target)}.</p> - */ - @NonNull - public Palette.Builder addTarget(@NonNull final Target target) { - if (!mTargets.contains(target)) { - mTargets.add(target); - } - return this; - } - /** - * Clear all added targets. This includes any default targets added automatically by - * {@link Palette}. - */ - @NonNull - public Palette.Builder clearTargets() { - if (mTargets != null) { - mTargets.clear(); - } - return this; - } - - /** - * Generate and return the {@link Palette} synchronously. - */ + /** Generate and return the {@link Palette} synchronously. */ @NonNull public Palette generate() { - final TimingLogger logger = LOG_TIMINGS - ? new TimingLogger(LOG_TAG, "Generation") - : null; - - List<Palette.Swatch> swatches; + List<Swatch> swatches; if (mBitmap != null) { // We have a Bitmap so we need to use quantization to reduce the number of colors // First we'll scale down the bitmap if needed - final Bitmap bitmap = scaleBitmapDown(mBitmap); - - if (logger != null) { - logger.addSplit("Processed Bitmap"); - } + Bitmap bitmap = scaleBitmapDown(mBitmap); - final Rect region = mRegion; + Rect region = mRegion; if (bitmap != mBitmap && region != null) { // If we have a scaled bitmap and a selected region, we need to scale down the // region to match the new scale - final double scale = bitmap.getWidth() / (double) mBitmap.getWidth(); + double scale = bitmap.getWidth() / (double) mBitmap.getWidth(); region.left = (int) Math.floor(region.left * scale); region.top = (int) Math.floor(region.top * scale); region.right = Math.min((int) Math.ceil(region.right * scale), @@ -832,54 +340,47 @@ public final class Palette { } // Now generate a quantizer from the Bitmap - if (mQuantizer == null) { - mQuantizer = new ColorCutQuantizer(); - } - mQuantizer.quantize(getPixelsFromBitmap(bitmap), - mMaxColors, mFilters.isEmpty() ? null : - mFilters.toArray(new Palette.Filter[mFilters.size()])); + mQuantizer.quantize( + getPixelsFromBitmap(bitmap), + mMaxColors); // If created a new bitmap, recycle it if (bitmap != mBitmap) { bitmap.recycle(); } - swatches = mQuantizer.getQuantizedColors(); - - if (logger != null) { - logger.addSplit("Color quantization completed"); - } - } else { + } else if (mSwatches != null) { // Else we're using the provided swatches swatches = mSwatches; + } else { + // The constructors enforce either a bitmap or swatches are present. + throw new AssertionError(); } // Now create a Palette instance - final Palette p = new Palette(swatches, mTargets); + Palette p = new Palette(swatches); // And make it generate itself - p.generate(); - - if (logger != null) { - logger.addSplit("Created Palette"); - logger.dumpToLog(); - } return p; } /** - * Generate the {@link Palette} asynchronously. The provided listener's - * {@link Palette.PaletteAsyncListener#onGenerated} method will be called with the palette when - * generated. + * Generate the {@link Palette} asynchronously. The provided listener's {@link + * PaletteAsyncListener#onGenerated} method will be called with the palette when generated. + * + * @deprecated Use the standard <code>java.util.concurrent</code> or <a + * href="https://developer.android.com/topic/libraries/architecture/coroutines">Kotlin + * concurrency utilities</a> to call {@link #generate()} instead. */ @NonNull - public AsyncTask<Bitmap, Void, Palette> generate(final Palette.PaletteAsyncListener listener) { - if (listener == null) { - throw new IllegalArgumentException("listener can not be null"); - } + @Deprecated + public android.os.AsyncTask<Bitmap, Void, Palette> generate( + @NonNull PaletteAsyncListener listener) { + assert (listener != null); - return new AsyncTask<Bitmap, Void, Palette>() { + return new android.os.AsyncTask<Bitmap, Void, Palette>() { @Override + @Nullable protected Palette doInBackground(Bitmap... params) { try { return generate(); @@ -890,16 +391,16 @@ public final class Palette { } @Override - protected void onPostExecute(Palette colorExtractor) { + protected void onPostExecute(@Nullable Palette colorExtractor) { listener.onGenerated(colorExtractor); } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mBitmap); + }.executeOnExecutor(android.os.AsyncTask.THREAD_POOL_EXECUTOR, mBitmap); } private int[] getPixelsFromBitmap(Bitmap bitmap) { - final int bitmapWidth = bitmap.getWidth(); - final int bitmapHeight = bitmap.getHeight(); - final int[] pixels = new int[bitmapWidth * bitmapHeight]; + int bitmapWidth = bitmap.getWidth(); + int bitmapHeight = bitmap.getHeight(); + int[] pixels = new int[bitmapWidth * bitmapHeight]; bitmap.getPixels(pixels, 0, bitmapWidth, 0, 0, bitmapWidth, bitmapHeight); if (mRegion == null) { @@ -908,32 +409,34 @@ public final class Palette { } else { // If we do have a region, lets create a subset array containing only the region's // pixels - final int regionWidth = mRegion.width(); - final int regionHeight = mRegion.height(); + int regionWidth = mRegion.width(); + int regionHeight = mRegion.height(); // pixels contains all of the pixels, so we need to iterate through each row and // copy the regions pixels into a new smaller array - final int[] subsetPixels = new int[regionWidth * regionHeight]; + int[] subsetPixels = new int[regionWidth * regionHeight]; for (int row = 0; row < regionHeight; row++) { - System.arraycopy(pixels, ((row + mRegion.top) * bitmapWidth) + mRegion.left, - subsetPixels, row * regionWidth, regionWidth); + System.arraycopy( + pixels, + ((row + mRegion.top) * bitmapWidth) + mRegion.left, + subsetPixels, + row * regionWidth, + regionWidth); } return subsetPixels; } } - /** - * Scale the bitmap down as needed. - */ - private Bitmap scaleBitmapDown(final Bitmap bitmap) { + /** Scale the bitmap down as needed. */ + private Bitmap scaleBitmapDown(Bitmap bitmap) { double scaleRatio = -1; if (mResizeArea > 0) { - final int bitmapArea = bitmap.getWidth() * bitmap.getHeight(); + int bitmapArea = bitmap.getWidth() * bitmap.getHeight(); if (bitmapArea > mResizeArea) { scaleRatio = Math.sqrt(mResizeArea / (double) bitmapArea); } } else if (mResizeMaxDimension > 0) { - final int maxDimension = Math.max(bitmap.getWidth(), bitmap.getHeight()); + int maxDimension = Math.max(bitmap.getWidth(), bitmap.getHeight()); if (maxDimension > mResizeMaxDimension) { scaleRatio = mResizeMaxDimension / (double) maxDimension; } @@ -944,11 +447,13 @@ public final class Palette { return bitmap; } - return Bitmap.createScaledBitmap(bitmap, + return Bitmap.createScaledBitmap( + bitmap, (int) Math.ceil(bitmap.getWidth() * scaleRatio), (int) Math.ceil(bitmap.getHeight() * scaleRatio), false); } + } /** @@ -961,9 +466,7 @@ public final class Palette { * * @param rgb the color in RGB888. * @param hsl HSL representation of the color. - * * @return true if the color is allowed, false if not. - * * @see Palette.Builder#addFilter(Palette.Filter) */ boolean isAllowed(int rgb, float[] hsl); @@ -1004,3 +507,4 @@ public final class Palette { } }; } + diff --git a/core/java/com/android/internal/graphics/palette/Quantizer.java b/core/java/com/android/internal/graphics/palette/Quantizer.java index db60f2e9dc69..a219ea3aa7d0 100644 --- a/core/java/com/android/internal/graphics/palette/Quantizer.java +++ b/core/java/com/android/internal/graphics/palette/Quantizer.java @@ -22,6 +22,15 @@ import java.util.List; * Definition of an algorithm that receives pixels and outputs a list of colors. */ public interface Quantizer { - void quantize(final int[] pixels, final int maxColors, final Palette.Filter[] filters); + /** + * Create colors representative of the colors present in pixels. + * @param pixels Set of ARGB representation of a color. + * @param maxColors number of colors to generate + */ + void quantize(int[] pixels, int maxColors); + + /** + * List of colors generated by previous call to quantize. + */ List<Palette.Swatch> getQuantizedColors(); } diff --git a/core/java/com/android/internal/graphics/palette/Target.java b/core/java/com/android/internal/graphics/palette/Target.java index 0540d80ef6f0..96e7faa81c3a 100644 --- a/core/java/com/android/internal/graphics/palette/Target.java +++ b/core/java/com/android/internal/graphics/palette/Target.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,368 +16,234 @@ package com.android.internal.graphics.palette; -/* - * Copyright 2015 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. - */ import android.annotation.FloatRange; +import android.annotation.IntRange; +import android.annotation.NonNull; /** - * Copied from: frameworks/support/v7/palette/src/main/java/android/support/v7/graphics/Target.java - * - * A class which allows custom selection of colors in a {@link Palette}'s generation. Instances - * can be created via the {@link android.support.v7.graphics.Target.Builder} class. - * - * <p>To use the target, use the {@link Palette.Builder#addTarget(Target)} API when building a - * Palette.</p> + * A class which allows custom selection of colors in a {@link Palette}'s generation. Instances can + * be created via the {@link Builder} class. */ -public final class Target { - - private static final float TARGET_DARK_LUMA = 0.26f; - private static final float MAX_DARK_LUMA = 0.45f; - - private static final float MIN_LIGHT_LUMA = 0.55f; - private static final float TARGET_LIGHT_LUMA = 0.74f; - - private static final float MIN_NORMAL_LUMA = 0.3f; - private static final float TARGET_NORMAL_LUMA = 0.5f; - private static final float MAX_NORMAL_LUMA = 0.7f; - - private static final float TARGET_MUTED_SATURATION = 0.3f; - private static final float MAX_MUTED_SATURATION = 0.4f; - - private static final float TARGET_VIBRANT_SATURATION = 1f; - private static final float MIN_VIBRANT_SATURATION = 0.35f; - - private static final float WEIGHT_SATURATION = 0.24f; - private static final float WEIGHT_LUMA = 0.52f; - private static final float WEIGHT_POPULATION = 0.24f; - - static final int INDEX_MIN = 0; - static final int INDEX_TARGET = 1; - static final int INDEX_MAX = 2; - - static final int INDEX_WEIGHT_SAT = 0; - static final int INDEX_WEIGHT_LUMA = 1; - static final int INDEX_WEIGHT_POP = 2; - - /** - * A target which has the characteristics of a vibrant color which is light in luminance. - */ - public static final Target LIGHT_VIBRANT; - - /** - * A target which has the characteristics of a vibrant color which is neither light or dark. - */ - public static final Target VIBRANT; - - /** - * A target which has the characteristics of a vibrant color which is dark in luminance. - */ - public static final Target DARK_VIBRANT; - - /** - * A target which has the characteristics of a muted color which is light in luminance. - */ - public static final Target LIGHT_MUTED; - - /** - * A target which has the characteristics of a muted color which is neither light or dark. - */ - public static final Target MUTED; - - /** - * A target which has the characteristics of a muted color which is dark in luminance. - */ - public static final Target DARK_MUTED; - static { - LIGHT_VIBRANT = new Target(); - setDefaultLightLightnessValues(LIGHT_VIBRANT); - setDefaultVibrantSaturationValues(LIGHT_VIBRANT); - - VIBRANT = new Target(); - setDefaultNormalLightnessValues(VIBRANT); - setDefaultVibrantSaturationValues(VIBRANT); - - DARK_VIBRANT = new Target(); - setDefaultDarkLightnessValues(DARK_VIBRANT); - setDefaultVibrantSaturationValues(DARK_VIBRANT); - - LIGHT_MUTED = new Target(); - setDefaultLightLightnessValues(LIGHT_MUTED); - setDefaultMutedSaturationValues(LIGHT_MUTED); - - MUTED = new Target(); - setDefaultNormalLightnessValues(MUTED); - setDefaultMutedSaturationValues(MUTED); - - DARK_MUTED = new Target(); - setDefaultDarkLightnessValues(DARK_MUTED); - setDefaultMutedSaturationValues(DARK_MUTED); - } - - final float[] mSaturationTargets = new float[3]; - final float[] mLightnessTargets = new float[3]; - final float[] mWeights = new float[3]; - boolean mIsExclusive = true; // default to true +public final class Target { + private static final float WEIGHT_CHROMA = 0.5f; + private static final float WEIGHT_RELATIVE_LUMINANCE = 0.5f; + private static final float WEIGHT_POPULATION = 0.3f; + private static final float WEIGHT_HUE = 0.2f; + + // Arbitrarily chosen, except max - CAM16 chroma has a ceiling of 130, based on unit testing. + private static final float DEFAULT_CHROMA_MIN = 0.f; + private static final float DEFAULT_CHROMA_MAX = 130.f; + private static final float DEFAULT_CHROMA_TARGET = 30.f; + + private float mTargetRelativeLuminance = -1.0f; + private float mChromaWeight; + private float mChromaTarget; + private float mChromaMin; + private float mChromaMax; + private float mRelativeLuminanceWeight; + private float mPopulationWeight; + private float mHueWeight; + private float mTargetHue; Target() { - setTargetDefaultValues(mSaturationTargets); - setTargetDefaultValues(mLightnessTargets); - setDefaultWeights(); + mChromaMax = DEFAULT_CHROMA_MAX; + mChromaMin = DEFAULT_CHROMA_MIN; + mChromaTarget = DEFAULT_CHROMA_TARGET; + mChromaWeight = WEIGHT_CHROMA; + mRelativeLuminanceWeight = WEIGHT_RELATIVE_LUMINANCE; + mPopulationWeight = WEIGHT_POPULATION; + mHueWeight = WEIGHT_HUE; } - Target(Target from) { - System.arraycopy(from.mSaturationTargets, 0, mSaturationTargets, 0, - mSaturationTargets.length); - System.arraycopy(from.mLightnessTargets, 0, mLightnessTargets, 0, - mLightnessTargets.length); - System.arraycopy(from.mWeights, 0, mWeights, 0, mWeights.length); + Target(@NonNull Target from) { + mTargetRelativeLuminance = from.mTargetRelativeLuminance; + mChromaWeight = from.mChromaWeight; + mRelativeLuminanceWeight = from.mRelativeLuminanceWeight; + mPopulationWeight = from.mPopulationWeight; + mHueWeight = from.mHueWeight; + mChromaTarget = from.mChromaTarget; + mChromaMin = from.mChromaMin; + mChromaMax = from.mChromaMax; } - /** - * The minimum saturation value for this target. - */ - @FloatRange(from = 0, to = 1) - public float getMinimumSaturation() { - return mSaturationTargets[INDEX_MIN]; + /** The relative luminance value for this target. */ + @FloatRange(from = 0, to = 100) + public float getTargetRelativeLuminance() { + return mTargetRelativeLuminance; } - /** - * The target saturation value for this target. - */ - @FloatRange(from = 0, to = 1) - public float getTargetSaturation() { - return mSaturationTargets[INDEX_TARGET]; + /** The relative luminance value for this target. */ + @FloatRange(from = 0, to = 100) + public float getTargetPerceptualLuminance() { + return Contrast.yToLstar(mTargetRelativeLuminance); } - /** - * The maximum saturation value for this target. - */ - @FloatRange(from = 0, to = 1) - public float getMaximumSaturation() { - return mSaturationTargets[INDEX_MAX]; + /** The minimum chroma value for this target. */ + @FloatRange(from = 0, to = 100) + public float getMinimumChroma() { + return mChromaMin; } - /** - * The minimum lightness value for this target. - */ - @FloatRange(from = 0, to = 1) - public float getMinimumLightness() { - return mLightnessTargets[INDEX_MIN]; + /** The target chroma value for this target. */ + @FloatRange(from = 0, to = 100) + public float getTargetChroma() { + return mChromaTarget; } - /** - * The target lightness value for this target. - */ - @FloatRange(from = 0, to = 1) - public float getTargetLightness() { - return mLightnessTargets[INDEX_TARGET]; + /** The maximum chroma value for this target. */ + @FloatRange(from = 0, to = 130) + public float getMaximumChroma() { + return mChromaMax; } - /** - * The maximum lightness value for this target. - */ - @FloatRange(from = 0, to = 1) - public float getMaximumLightness() { - return mLightnessTargets[INDEX_MAX]; + /** The target hue value for this target. */ + @FloatRange(from = 0, to = 100) + public float getTargetHue() { + return mTargetHue; } /** - * Returns the weight of importance that this target places on a color's saturation within - * the image. + * Returns the weight of importance that this target places on a color's chroma within the + * image. * * <p>The larger the weight, relative to the other weights, the more important that a color - * being close to the target value has on selection.</p> + * being + * close to the target value has on selection. * - * @see #getTargetSaturation() + * @see #getTargetChroma() */ - public float getSaturationWeight() { - return mWeights[INDEX_WEIGHT_SAT]; + public float getChromaWeight() { + return mChromaWeight; } /** - * Returns the weight of importance that this target places on a color's lightness within - * the image. + * Returns the weight of importance that this target places on a color's lightness within the + * image. * * <p>The larger the weight, relative to the other weights, the more important that a color - * being close to the target value has on selection.</p> + * being + * close to the target value has on selection. * - * @see #getTargetLightness() + * @see #getTargetRelativeLuminance() */ public float getLightnessWeight() { - return mWeights[INDEX_WEIGHT_LUMA]; + return mRelativeLuminanceWeight; } /** - * Returns the weight of importance that this target places on a color's population within - * the image. + * Returns the weight of importance that this target places on a color's population within the + * image. * - * <p>The larger the weight, relative to the other weights, the more important that a - * color's population being close to the most populous has on selection.</p> + * <p>The larger the weight, relative to the other weights, the more important that a color's + * population being close to the most populous has on selection. */ public float getPopulationWeight() { - return mWeights[INDEX_WEIGHT_POP]; + return mPopulationWeight; } /** - * Returns whether any color selected for this target is exclusive for this target only. + * Returns the weight of importance that this target places on a color's hue. * - * <p>If false, then the color can be selected for other targets.</p> + * <p>The larger the weight, relative to the other weights, the more important that a color's + * hue being close to the desired hue has on selection. */ - public boolean isExclusive() { - return mIsExclusive; + public float getHueWeight() { + return mHueWeight; } - private static void setTargetDefaultValues(final float[] values) { - values[INDEX_MIN] = 0f; - values[INDEX_TARGET] = 0.5f; - values[INDEX_MAX] = 1f; - } - - private void setDefaultWeights() { - mWeights[INDEX_WEIGHT_SAT] = WEIGHT_SATURATION; - mWeights[INDEX_WEIGHT_LUMA] = WEIGHT_LUMA; - mWeights[INDEX_WEIGHT_POP] = WEIGHT_POPULATION; - } - void normalizeWeights() { - float sum = 0; - for (int i = 0, z = mWeights.length; i < z; i++) { - float weight = mWeights[i]; - if (weight > 0) { - sum += weight; - } - } - if (sum != 0) { - for (int i = 0, z = mWeights.length; i < z; i++) { - if (mWeights[i] > 0) { - mWeights[i] /= sum; - } - } - } - } - - private static void setDefaultDarkLightnessValues(Target target) { - target.mLightnessTargets[INDEX_TARGET] = TARGET_DARK_LUMA; - target.mLightnessTargets[INDEX_MAX] = MAX_DARK_LUMA; - } - - private static void setDefaultNormalLightnessValues(Target target) { - target.mLightnessTargets[INDEX_MIN] = MIN_NORMAL_LUMA; - target.mLightnessTargets[INDEX_TARGET] = TARGET_NORMAL_LUMA; - target.mLightnessTargets[INDEX_MAX] = MAX_NORMAL_LUMA; - } - - private static void setDefaultLightLightnessValues(Target target) { - target.mLightnessTargets[INDEX_MIN] = MIN_LIGHT_LUMA; - target.mLightnessTargets[INDEX_TARGET] = TARGET_LIGHT_LUMA; - } - - private static void setDefaultVibrantSaturationValues(Target target) { - target.mSaturationTargets[INDEX_MIN] = MIN_VIBRANT_SATURATION; - target.mSaturationTargets[INDEX_TARGET] = TARGET_VIBRANT_SATURATION; - } - - private static void setDefaultMutedSaturationValues(Target target) { - target.mSaturationTargets[INDEX_TARGET] = TARGET_MUTED_SATURATION; - target.mSaturationTargets[INDEX_MAX] = MAX_MUTED_SATURATION; - } - - /** - * Builder class for generating custom {@link Target} instances. - */ - public final static class Builder { + /** Builder class for generating custom {@link Target} instances. */ + public static class Builder { private final Target mTarget; - /** - * Create a new {@link Target} builder from scratch. - */ + /** Create a new {@link Target} builder from scratch. */ public Builder() { mTarget = new Target(); } - /** - * Create a new builder based on an existing {@link Target}. - */ - public Builder(Target target) { + /** Create a new builder based on an existing {@link Target}. */ + public Builder(@NonNull Target target) { mTarget = new Target(target); } - /** - * Set the minimum saturation value for this target. - */ - public Target.Builder setMinimumSaturation(@FloatRange(from = 0, to = 1) float value) { - mTarget.mSaturationTargets[INDEX_MIN] = value; + /** Set the minimum chroma value for this target. */ + @NonNull + public Builder setMinimumChroma(@FloatRange(from = 0, to = 100) float value) { + mTarget.mChromaMin = value; return this; } - /** - * Set the target/ideal saturation value for this target. - */ - public Target.Builder setTargetSaturation(@FloatRange(from = 0, to = 1) float value) { - mTarget.mSaturationTargets[INDEX_TARGET] = value; + /** Set the target/ideal chroma value for this target. */ + @NonNull + public Builder setTargetChroma(@FloatRange(from = 0, to = 100) float value) { + mTarget.mChromaTarget = value; return this; } - /** - * Set the maximum saturation value for this target. - */ - public Target.Builder setMaximumSaturation(@FloatRange(from = 0, to = 1) float value) { - mTarget.mSaturationTargets[INDEX_MAX] = value; + /** Set the maximum chroma value for this target. */ + @NonNull + public Builder setMaximumChroma(@FloatRange(from = 0, to = 100) float value) { + mTarget.mChromaMax = value; return this; } - /** - * Set the minimum lightness value for this target. - */ - public Target.Builder setMinimumLightness(@FloatRange(from = 0, to = 1) float value) { - mTarget.mLightnessTargets[INDEX_MIN] = value; + /** Set the minimum lightness value for this target, using Y in XYZ color space. */ + @NonNull + public Builder setTargetRelativeLuminance(@FloatRange(from = 0, to = 100) float value) { + mTarget.mTargetRelativeLuminance = value; return this; } - /** - * Set the target/ideal lightness value for this target. - */ - public Target.Builder setTargetLightness(@FloatRange(from = 0, to = 1) float value) { - mTarget.mLightnessTargets[INDEX_TARGET] = value; + /** Set the minimum lightness value for this target, using L* in LAB color space. */ + @NonNull + public Builder setTargetPerceptualLuminance(@FloatRange(from = 0, to = 100) float value) { + mTarget.mTargetRelativeLuminance = Contrast.lstarToY(value); return this; } /** - * Set the maximum lightness value for this target. + * Set the hue desired from the target. This hue is not enforced, the only consequence + * is points will be awarded to seed colors the closer they are to this hue. */ - public Target.Builder setMaximumLightness(@FloatRange(from = 0, to = 1) float value) { - mTarget.mLightnessTargets[INDEX_MAX] = value; + @NonNull + public Builder setTargetHue(@IntRange(from = 0, to = 360) int hue) { + mTarget.mTargetHue = hue; + return this; + } + + /** Sets lightness value for this target. */ + @NonNull + public Builder setContrastRatio( + @FloatRange(from = 1, to = 21) float value, + @FloatRange(from = 0, to = 100) float relativeLuminance) { + float counterpartY = relativeLuminance; + float lstar = Contrast.yToLstar(counterpartY); + + float targetY; + if (lstar < 50) { + targetY = Contrast.lighterY(counterpartY, value); + } else { + targetY = Contrast.darkerY(counterpartY, value); + } + mTarget.mTargetRelativeLuminance = targetY; return this; } /** - * Set the weight of importance that this target will place on saturation values. + * Set the weight of importance that this target will place on chroma values. * * <p>The larger the weight, relative to the other weights, the more important that a color - * being close to the target value has on selection.</p> + * being close to the target value has on selection. * - * <p>A weight of 0 means that it has no weight, and thus has no - * bearing on the selection.</p> + * <p>A weight of 0 means that it has no weight, and thus has no bearing on the selection. * - * @see #setTargetSaturation(float) + * @see #setTargetChroma(float) */ - public Target.Builder setSaturationWeight(@FloatRange(from = 0) float weight) { - mTarget.mWeights[INDEX_WEIGHT_SAT] = weight; + @NonNull + public Builder setChromaWeight(@FloatRange(from = 0) float weight) { + mTarget.mChromaWeight = weight; return this; } @@ -385,51 +251,40 @@ public final class Target { * Set the weight of importance that this target will place on lightness values. * * <p>The larger the weight, relative to the other weights, the more important that a color - * being close to the target value has on selection.</p> + * being close to the target value has on selection. * - * <p>A weight of 0 means that it has no weight, and thus has no - * bearing on the selection.</p> + * <p>A weight of 0 means that it has no weight, and thus has no bearing on the selection. * - * @see #setTargetLightness(float) + * @see #setTargetRelativeLuminance(float) */ - public Target.Builder setLightnessWeight(@FloatRange(from = 0) float weight) { - mTarget.mWeights[INDEX_WEIGHT_LUMA] = weight; + @NonNull + public Builder setLightnessWeight(@FloatRange(from = 0) float weight) { + mTarget.mRelativeLuminanceWeight = weight; return this; } /** * Set the weight of importance that this target will place on a color's population within - * the image. + * the + * image. * * <p>The larger the weight, relative to the other weights, the more important that a - * color's population being close to the most populous has on selection.</p> + * color's + * population being close to the most populous has on selection. * - * <p>A weight of 0 means that it has no weight, and thus has no - * bearing on the selection.</p> + * <p>A weight of 0 means that it has no weight, and thus has no bearing on the selection. */ - public Target.Builder setPopulationWeight(@FloatRange(from = 0) float weight) { - mTarget.mWeights[INDEX_WEIGHT_POP] = weight; + @NonNull + public Builder setPopulationWeight(@FloatRange(from = 0) float weight) { + mTarget.mPopulationWeight = weight; return this; } - /** - * Set whether any color selected for this target is exclusive to this target only. - * Defaults to true. - * - * @param exclusive true if any the color is exclusive to this target, or false is the - * color can be selected for other targets. - */ - public Target.Builder setExclusive(boolean exclusive) { - mTarget.mIsExclusive = exclusive; - return this; - } - /** - * Builds and returns the resulting {@link Target}. - */ + /** Builds and returns the resulting {@link Target}. */ + @NonNull public Target build() { return mTarget; } } - -}
\ No newline at end of file +} diff --git a/core/java/com/android/internal/graphics/palette/VariationalKMeansQuantizer.java b/core/java/com/android/internal/graphics/palette/VariationalKMeansQuantizer.java index b0355350dc15..d791f7b3e6be 100644 --- a/core/java/com/android/internal/graphics/palette/VariationalKMeansQuantizer.java +++ b/core/java/com/android/internal/graphics/palette/VariationalKMeansQuantizer.java @@ -70,10 +70,9 @@ public class VariationalKMeansQuantizer implements Quantizer { * * @param pixels Pixels to quantize. * @param maxColors Maximum number of clusters to extract. - * @param filters Colors that should be ignored */ @Override - public void quantize(int[] pixels, int maxColors, Palette.Filter[] filters) { + public void quantize(int[] pixels, int maxColors) { // Start by converting all colors to HSL. // HLS is way more meaningful for clustering than RGB. final float[] hsl = {0, 0, 0}; @@ -111,16 +110,18 @@ public class VariationalKMeansQuantizer implements Quantizer { // Convert data to final format, de-normalizing the hue. mQuantizedColors = new ArrayList<>(); + float[] mHsl = new float[3]; for (KMeans.Mean mean : optimalMeans) { if (mean.getItems().size() == 0) { continue; } float[] centroid = mean.getCentroid(); - mQuantizedColors.add(new Palette.Swatch(new float[]{ - centroid[0] * 360f, - centroid[1], - centroid[2] - }, mean.getItems().size())); + + mHsl[0] = centroid[0] * 360f; + mHsl[1] = centroid[1]; + mHsl[2] = centroid[2]; + int color = ColorUtils.HSLToColor(mHsl); + mQuantizedColors.add(new Palette.Swatch(color, mean.getItems().size())); } } diff --git a/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java b/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java new file mode 100644 index 000000000000..a87a34f4ae11 --- /dev/null +++ b/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java @@ -0,0 +1,269 @@ +/* + * 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.internal.graphics.palette; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +/** + * A color quantizer based on the Kmeans algorithm. + * + * This is an implementation of Kmeans based on Celebi's 2011 paper, + * "Improving the Performance of K-Means for Color Quantization". In the paper, this algorithm is + * referred to as "WSMeans", or, "Weighted Square Means" The main advantages of this Kmeans + * implementation are taking advantage of triangle properties to avoid distance calculations, as + * well as indexing colors by their count, thus minimizing the number of points to move around. + * + * Celebi's paper also stabilizes results and guarantees high quality by using starting centroids + * from Wu's quantization algorithm. See CelebiQuantizer for more info. + */ +public class WSMeansQuantizer implements Quantizer { + Mean[] mMeans; + private final Map<Integer, Integer> mCountByColor = new HashMap<>(); + private final Map<Integer, Integer> mMeanIndexByColor = new HashMap<>(); + private final Set<Integer> mUniqueColors = new HashSet<>(); + private final List<Palette.Swatch> mSwatches = new ArrayList<>(); + private final CentroidProvider mCentroidProvider; + + public WSMeansQuantizer( + float[][] means, CentroidProvider centroidProvider, int[] pixels, int maxColors) { + if (pixels == null) { + pixels = new int[]{}; + } + mCentroidProvider = centroidProvider; + mMeans = new Mean[maxColors]; + for (int i = 0; i < means.length; i++) { + mMeans[i] = new Mean(means[i]); + } + + if (maxColors > means.length) { + int randomMeansToCreate = maxColors - means.length; + for (int i = 0; i < randomMeansToCreate; i++) { + mMeans[means.length + i] = new Mean(100); + } + } + + for (int pixel : pixels) { + Integer currentCount = mCountByColor.get(pixel); + if (currentCount == null) { + currentCount = 0; + mUniqueColors.add(pixel); + } + mCountByColor.put(pixel, currentCount + 1); + } + for (int color : mUniqueColors) { + int closestMeanIndex = -1; + double closestMeanDistance = -1; + float[] centroid = mCentroidProvider.getCentroid(color); + for (int i = 0; i < mMeans.length; i++) { + double distance = mCentroidProvider.distance(centroid, mMeans[i].center); + if (closestMeanIndex == -1 || distance < closestMeanDistance) { + closestMeanIndex = i; + closestMeanDistance = distance; + } + } + mMeanIndexByColor.put(color, closestMeanIndex); + } + + if (pixels.length == 0) { + return; + } + + predict(maxColors, 0); + } + + /** Create starting centroids for K-means from a set of colors. */ + public static float[][] createStartingCentroids(CentroidProvider centroidProvider, + List<Palette.Swatch> swatches) { + float[][] startingCentroids = new float[swatches.size()][]; + for (int i = 0; i < swatches.size(); i++) { + startingCentroids[i] = centroidProvider.getCentroid(swatches.get(i).getInt()); + } + return startingCentroids; + } + + /** Create random starting centroids for K-means. */ + public static float[][] randomMeans(int maxColors, int upperBound) { + float[][] means = new float[maxColors][]; + for (int i = 0; i < maxColors; i++) { + means[i] = new Mean(upperBound).center; + } + return means; + } + + + @Override + public void quantize(int[] pixels, int maxColors) { + + } + + @Override + public List<Palette.Swatch> getQuantizedColors() { + return mSwatches; + } + + private void predict(int maxColors, int iterationsCompleted) { + double[][] centroidDistance = new double[maxColors][maxColors]; + for (int i = 0; i <= maxColors; i++) { + for (int j = i + 1; j < maxColors; j++) { + float[] meanI = mMeans[i].center; + float[] meanJ = mMeans[j].center; + double distance = mCentroidProvider.distance(meanI, meanJ); + centroidDistance[i][j] = distance; + centroidDistance[j][i] = distance; + } + } + + // Construct a K×K matrix M in which row i is a permutation of + // 1,2,…,K that represents the clusters in increasing order of + // distance of their centers from ci; + int[][] distanceMatrix = new int[maxColors][maxColors]; + for (int i = 0; i < maxColors; i++) { + double[] distancesFromIToAnotherMean = centroidDistance[i]; + double[] sortedByDistanceAscending = distancesFromIToAnotherMean.clone(); + Arrays.sort(sortedByDistanceAscending); + int[] outputRow = new int[maxColors]; + for (int j = 0; j < maxColors; j++) { + outputRow[j] = findIndex(distancesFromIToAnotherMean, sortedByDistanceAscending[j]); + } + distanceMatrix[i] = outputRow; + } + + // for (i=1;i≤N′;i=i+ 1) do + // Let Sp be the cluster that xi was assigned to in the previous + // iteration; + // p=m[i]; + // min_dist=prev_dist=jjxi−cpjj2; + boolean anyColorMoved = false; + for (int intColor : mUniqueColors) { + float[] color = mCentroidProvider.getCentroid(intColor); + int indexOfCurrentMean = mMeanIndexByColor.get(intColor); + Mean currentMean = mMeans[indexOfCurrentMean]; + double minDistance = mCentroidProvider.distance(color, currentMean.center); + for (int j = 1; j < maxColors; j++) { + int indexOfClusterFromCurrentToJ = distanceMatrix[indexOfCurrentMean][j]; + double distanceBetweenJAndCurrent = + centroidDistance[indexOfCurrentMean][indexOfClusterFromCurrentToJ]; + if (distanceBetweenJAndCurrent >= (4 * minDistance)) { + break; + } + double distanceBetweenJAndColor = mCentroidProvider.distance(mMeans[j].center, + color); + if (distanceBetweenJAndColor < minDistance) { + minDistance = distanceBetweenJAndColor; + mMeanIndexByColor.remove(intColor); + mMeanIndexByColor.put(intColor, j); + anyColorMoved = true; + } + } + } + + List<MeanBucket> buckets = new ArrayList<>(); + for (int i = 0; i < maxColors; i++) { + buckets.add(new MeanBucket()); + } + + for (int intColor : mUniqueColors) { + int meanIndex = mMeanIndexByColor.get(intColor); + MeanBucket meanBucket = buckets.get(meanIndex); + meanBucket.add(mCentroidProvider.getCentroid(intColor), intColor, + mCountByColor.get(intColor)); + } + + List<Palette.Swatch> swatches = new ArrayList<>(); + boolean done = !anyColorMoved && iterationsCompleted > 0 || iterationsCompleted >= 100; + if (done) { + for (int i = 0; i < buckets.size(); i++) { + MeanBucket a = buckets.get(i); + if (a.mCount <= 0) { + continue; + } + List<MeanBucket> bucketsToMerge = new ArrayList<>(); + for (int j = i + 1; j < buckets.size(); j++) { + MeanBucket b = buckets.get(j); + if (b.mCount == 0) { + continue; + } + float[] bCentroid = b.getCentroid(); + assert (a.mCount > 0); + assert (a.getCentroid() != null); + + assert (bCentroid != null); + if (mCentroidProvider.distance(a.getCentroid(), b.getCentroid()) < 5) { + bucketsToMerge.add(b); + } + } + + for (MeanBucket bucketToMerge : bucketsToMerge) { + float[] centroid = bucketToMerge.getCentroid(); + a.add(centroid, mCentroidProvider.getColor(centroid), bucketToMerge.mCount); + buckets.remove(bucketToMerge); + } + } + + for (MeanBucket bucket : buckets) { + float[] centroid = bucket.getCentroid(); + if (centroid == null) { + continue; + } + + int rgb = mCentroidProvider.getColor(centroid); + swatches.add(new Palette.Swatch(rgb, bucket.mCount)); + mSwatches.clear(); + mSwatches.addAll(swatches); + } + } else { + List<MeanBucket> emptyBuckets = new ArrayList<>(); + for (int i = 0; i < buckets.size(); i++) { + MeanBucket bucket = buckets.get(i); + if ((bucket.getCentroid() == null) || (bucket.mCount == 0)) { + emptyBuckets.add(bucket); + for (Integer color : mUniqueColors) { + int meanIndex = mMeanIndexByColor.get(color); + if (meanIndex > i) { + mMeanIndexByColor.put(color, meanIndex--); + } + } + } + } + + Mean[] newMeans = new Mean[buckets.size()]; + for (int i = 0; i < buckets.size(); i++) { + float[] centroid = buckets.get(i).getCentroid(); + newMeans[i] = new Mean(centroid); + } + + predict(buckets.size(), iterationsCompleted + 1); + } + + } + + private static int findIndex(double[] list, double element) { + for (int i = 0; i < list.length; i++) { + if (list[i] == element) { + return i; + } + } + throw new IllegalArgumentException("Element not in list"); + } +} diff --git a/core/java/com/android/internal/graphics/palette/WuQuantizer.java b/core/java/com/android/internal/graphics/palette/WuQuantizer.java new file mode 100644 index 000000000000..01e45f613986 --- /dev/null +++ b/core/java/com/android/internal/graphics/palette/WuQuantizer.java @@ -0,0 +1,442 @@ +/* + * 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.internal.graphics.palette; + + +import java.util.ArrayList; +import java.util.List; + +// All reference Wu implementations are based on the original C code by Wu. +// Comments on methods are the same as in the original implementation, and the comment below +// is the original class header. + +/** + * Wu's Color Quantizer (v. 2) (see Graphics Gems vol. II, pp. 126-133) Author: Xiaolin Wu + * + * <p>Algorithm: Greedy orthogonal bipartition of RGB space for variance minimization aided by + * inclusion-exclusion tricks. For speed no nearest neighbor search is done. Slightly better + * performance can be expected by more sophisticated but more expensive versions. + */ +public class WuQuantizer implements Quantizer { + private static final int MAX_COLORS = 256; + private static final int RED = 2; + private static final int GREEN = 1; + private static final int BLUE = 0; + + private static final int QUANT_SIZE = 33; + private final List<Palette.Swatch> mSwatches = new ArrayList<>(); + + @Override + public List<Palette.Swatch> getQuantizedColors() { + return mSwatches; + } + + private static final class Box { + int mR0; /* min value, exclusive */ + int mR1; /* max value, inclusive */ + int mG0; + int mG1; + int mB0; + int mB1; + int mVol; + } + + private final int mSize; /* image size, in bytes. */ + private int mMaxColors; + private int[] mQadd; + private final int[] mPixels; + + private final double[][][] mM2 = new double[QUANT_SIZE][QUANT_SIZE][QUANT_SIZE]; + private final long[][][] mWt = new long[QUANT_SIZE][QUANT_SIZE][QUANT_SIZE]; + private final long[][][] mMr = new long[QUANT_SIZE][QUANT_SIZE][QUANT_SIZE]; + private final long[][][] mMg = new long[QUANT_SIZE][QUANT_SIZE][QUANT_SIZE]; + private final long[][][] mMb = new long[QUANT_SIZE][QUANT_SIZE][QUANT_SIZE]; + + public WuQuantizer(int[] pixels, int maxColorCount) { + if (pixels == null) { + pixels = new int[]{}; + } + this.mPixels = pixels; + this.mSize = pixels.length; + } + + @Override + public void quantize(int[] colors, int maxColorCount) { + // All of the sample Wu implementations are reimplementations of a snippet of C code from + // the early 90s. They all cap the maximum # of colors at 256, and it is impossible to tell + // if this is a requirement, a consequence of QUANT_SIZE, or arbitrary. + this.mMaxColors = Math.min(MAX_COLORS, maxColorCount); + Box[] cube = new Box[mMaxColors]; + int red, green, blue; + + int next, i, k; + long weight; + double[] vv = new double[mMaxColors]; + double temp; + + compute3DHistogram(mWt, mMr, mMg, mMb, mM2); + computeMoments(mWt, mMr, mMg, mMb, mM2); + + for (i = 0; i < mMaxColors; i++) { + cube[i] = new Box(); + } + + cube[0].mR0 = cube[0].mG0 = cube[0].mB0 = 0; + cube[0].mR1 = cube[0].mG1 = cube[0].mB1 = QUANT_SIZE - 1; + next = 0; + + for (i = 1; i < mMaxColors; ++i) { + if (cut(cube[next], cube[i])) { + vv[next] = (cube[next].mVol > 1) ? getVariance(cube[next]) : 0.0f; + vv[i] = (cube[i].mVol > 1) ? getVariance(cube[i]) : 0.0f; + } else { + vv[next] = 0.0f; + i--; + } + next = 0; + temp = vv[0]; + for (k = 1; k <= i; ++k) { + if (vv[k] > temp) { + temp = vv[k]; + next = k; + } + } + if (temp <= 0.0f) { + break; + } + } + + for (k = 0; k < mMaxColors; ++k) { + weight = getVolume(cube[k], mWt); + if (weight > 0) { + red = (int) (getVolume(cube[k], mMr) / weight); + green = (int) (getVolume(cube[k], mMg) / weight); + blue = (int) (getVolume(cube[k], mMb) / weight); + colors[k] = ((red & 0x0ff) << 16) | ((green & 0x0ff) << 8) | (blue & 0x0ff); + } else { + colors[k] = 0; + } + } + + int bitsPerPixel = 0; + while ((1 << bitsPerPixel) < mMaxColors) { + bitsPerPixel++; + } + + List<Palette.Swatch> swatches = new ArrayList<>(); + for (int l = 0; l < k; l++) { + int pixel = colors[l]; + if (pixel == 0) { + continue; + } + swatches.add(new Palette.Swatch(pixel, 0)); + } + mSwatches.clear(); + mSwatches.addAll(swatches); + } + + /* Histogram is in elements 1..HISTSIZE along each axis, + * element 0 is for base or marginal value + * NB: these must start out 0! + */ + private void compute3DHistogram( + long[][][] vwt, long[][][] vmr, long[][][] vmg, long[][][] vmb, double[][][] m2) { + // build 3-D color histogram of counts, r/g/b, and c^2 + int r, g, b; + int i; + int inr; + int ing; + int inb; + int[] table = new int[256]; + + for (i = 0; i < 256; i++) { + table[i] = i * i; + } + + mQadd = new int[mSize]; + + for (i = 0; i < mSize; ++i) { + int rgb = mPixels[i]; + // Skip less than opaque pixels. They're not meaningful in the context of palette + // generation for UI schemes. + if ((rgb >>> 24) < 0xff) { + continue; + } + r = ((rgb >> 16) & 0xff); + g = ((rgb >> 8) & 0xff); + b = (rgb & 0xff); + inr = (r >> 3) + 1; + ing = (g >> 3) + 1; + inb = (b >> 3) + 1; + mQadd[i] = (inr << 10) + (inr << 6) + inr + (ing << 5) + ing + inb; + /*[inr][ing][inb]*/ + ++vwt[inr][ing][inb]; + vmr[inr][ing][inb] += r; + vmg[inr][ing][inb] += g; + vmb[inr][ing][inb] += b; + m2[inr][ing][inb] += table[r] + table[g] + table[b]; + } + } + + /* At conclusion of the histogram step, we can interpret + * wt[r][g][b] = sum over voxel of P(c) + * mr[r][g][b] = sum over voxel of r*P(c) , similarly for mg, mb + * m2[r][g][b] = sum over voxel of c^2*P(c) + * Actually each of these should be divided by 'size' to give the usual + * interpretation of P() as ranging from 0 to 1, but we needn't do that here. + * + * We now convert histogram into moments so that we can rapidly calculate + * the sums of the above quantities over any desired box. + */ + private void computeMoments( + long[][][] vwt, long[][][] vmr, long[][][] vmg, long[][][] vmb, double[][][] m2) { + /* compute cumulative moments. */ + int i, r, g, b; + int line, line_r, line_g, line_b; + int[] area = new int[QUANT_SIZE]; + int[] area_r = new int[QUANT_SIZE]; + int[] area_g = new int[QUANT_SIZE]; + int[] area_b = new int[QUANT_SIZE]; + double line2; + double[] area2 = new double[QUANT_SIZE]; + + for (r = 1; r < QUANT_SIZE; ++r) { + for (i = 0; i < QUANT_SIZE; ++i) { + area2[i] = area[i] = area_r[i] = area_g[i] = area_b[i] = 0; + } + for (g = 1; g < QUANT_SIZE; ++g) { + line2 = line = line_r = line_g = line_b = 0; + for (b = 1; b < QUANT_SIZE; ++b) { + line += vwt[r][g][b]; + line_r += vmr[r][g][b]; + line_g += vmg[r][g][b]; + line_b += vmb[r][g][b]; + line2 += m2[r][g][b]; + + area[b] += line; + area_r[b] += line_r; + area_g[b] += line_g; + area_b[b] += line_b; + area2[b] += line2; + + vwt[r][g][b] = vwt[r - 1][g][b] + area[b]; + vmr[r][g][b] = vmr[r - 1][g][b] + area_r[b]; + vmg[r][g][b] = vmg[r - 1][g][b] + area_g[b]; + vmb[r][g][b] = vmb[r - 1][g][b] + area_b[b]; + m2[r][g][b] = m2[r - 1][g][b] + area2[b]; + } + } + } + } + + private long getVolume(Box cube, long[][][] mmt) { + /* Compute sum over a box of any given statistic */ + return (mmt[cube.mR1][cube.mG1][cube.mB1] + - mmt[cube.mR1][cube.mG1][cube.mB0] + - mmt[cube.mR1][cube.mG0][cube.mB1] + + mmt[cube.mR1][cube.mG0][cube.mB0] + - mmt[cube.mR0][cube.mG1][cube.mB1] + + mmt[cube.mR0][cube.mG1][cube.mB0] + + mmt[cube.mR0][cube.mG0][cube.mB1] + - mmt[cube.mR0][cube.mG0][cube.mB0]); + } + + /* The next two routines allow a slightly more efficient calculation + * of Vol() for a proposed subbox of a given box. The sum of Top() + * and Bottom() is the Vol() of a subbox split in the given direction + * and with the specified new upper bound. + */ + private long getBottom(Box cube, int dir, long[][][] mmt) { + /* Compute part of Vol(cube, mmt) that doesn't depend on r1, g1, or b1 */ + /* (depending on dir) */ + switch (dir) { + case RED: + return (-mmt[cube.mR0][cube.mG1][cube.mB1] + + mmt[cube.mR0][cube.mG1][cube.mB0] + + mmt[cube.mR0][cube.mG0][cube.mB1] + - mmt[cube.mR0][cube.mG0][cube.mB0]); + case GREEN: + return (-mmt[cube.mR1][cube.mG0][cube.mB1] + + mmt[cube.mR1][cube.mG0][cube.mB0] + + mmt[cube.mR0][cube.mG0][cube.mB1] + - mmt[cube.mR0][cube.mG0][cube.mB0]); + case BLUE: + return (-mmt[cube.mR1][cube.mG1][cube.mB0] + + mmt[cube.mR1][cube.mG0][cube.mB0] + + mmt[cube.mR0][cube.mG1][cube.mB0] + - mmt[cube.mR0][cube.mG0][cube.mB0]); + default: + return 0; + } + } + + private long getTop(Box cube, int dir, int pos, long[][][] mmt) { + /* Compute remainder of Vol(cube, mmt), substituting pos for */ + /* r1, g1, or b1 (depending on dir) */ + switch (dir) { + case RED: + return (mmt[pos][cube.mG1][cube.mB1] + - mmt[pos][cube.mG1][cube.mB0] + - mmt[pos][cube.mG0][cube.mB1] + + mmt[pos][cube.mG0][cube.mB0]); + case GREEN: + return (mmt[cube.mR1][pos][cube.mB1] + - mmt[cube.mR1][pos][cube.mB0] + - mmt[cube.mR0][pos][cube.mB1] + + mmt[cube.mR0][pos][cube.mB0]); + case BLUE: + return (mmt[cube.mR1][cube.mG1][pos] + - mmt[cube.mR1][cube.mG0][pos] + - mmt[cube.mR0][cube.mG1][pos] + + mmt[cube.mR0][cube.mG0][pos]); + default: + return 0; + } + } + + private double getVariance(Box cube) { + /* Compute the weighted variance of a box */ + /* NB: as with the raw statistics, this is really the variance * size */ + double dr, dg, db, xx; + dr = getVolume(cube, mMr); + dg = getVolume(cube, mMg); + db = getVolume(cube, mMb); + xx = + mM2[cube.mR1][cube.mG1][cube.mB1] + - mM2[cube.mR1][cube.mG1][cube.mB0] + - mM2[cube.mR1][cube.mG0][cube.mB1] + + mM2[cube.mR1][cube.mG0][cube.mB0] + - mM2[cube.mR0][cube.mG1][cube.mB1] + + mM2[cube.mR0][cube.mG1][cube.mB0] + + mM2[cube.mR0][cube.mG0][cube.mB1] + - mM2[cube.mR0][cube.mG0][cube.mB0]; + return xx - (dr * dr + dg * dg + db * db) / getVolume(cube, mWt); + } + + /* We want to minimize the sum of the variances of two subboxes. + * The sum(c^2) terms can be ignored since their sum over both subboxes + * is the same (the sum for the whole box) no matter where we split. + * The remaining terms have a minus sign in the variance formula, + * so we drop the minus sign and MAXIMIZE the sum of the two terms. + */ + private double maximize( + Box cube, + int dir, + int first, + int last, + int[] cut, + long wholeR, + long wholeG, + long wholeB, + long wholeW) { + long half_r, half_g, half_b, half_w; + long base_r, base_g, base_b, base_w; + int i; + double temp, max; + + base_r = getBottom(cube, dir, mMr); + base_g = getBottom(cube, dir, mMg); + base_b = getBottom(cube, dir, mMb); + base_w = getBottom(cube, dir, mWt); + + max = 0.0f; + cut[0] = -1; + + for (i = first; i < last; ++i) { + half_r = base_r + getTop(cube, dir, i, mMr); + half_g = base_g + getTop(cube, dir, i, mMg); + half_b = base_b + getTop(cube, dir, i, mMb); + half_w = base_w + getTop(cube, dir, i, mWt); + /* now half_x is sum over lower half of box, if split at i */ + if (half_w == 0) /* subbox could be empty of pixels! */ { + continue; /* never split into an empty box */ + } + temp = (half_r * half_r + half_g * half_g + half_b * half_b) / (double) half_w; + half_r = wholeR - half_r; + half_g = wholeG - half_g; + half_b = wholeB - half_b; + half_w = wholeW - half_w; + if (half_w == 0) /* subbox could be empty of pixels! */ { + continue; /* never split into an empty box */ + } + temp += (half_r * half_r + half_g * half_g + half_b * half_b) / (double) half_w; + + if (temp > max) { + max = temp; + cut[0] = i; + } + } + + return max; + } + + private boolean cut(Box set1, Box set2) { + int dir; + int[] cutr = new int[1]; + int[] cutg = new int[1]; + int[] cutb = new int[1]; + double maxr, maxg, maxb; + long whole_r, whole_g, whole_b, whole_w; + + whole_r = getVolume(set1, mMr); + whole_g = getVolume(set1, mMg); + whole_b = getVolume(set1, mMb); + whole_w = getVolume(set1, mWt); + + maxr = maximize(set1, RED, set1.mR0 + 1, set1.mR1, cutr, whole_r, whole_g, whole_b, + whole_w); + maxg = maximize(set1, GREEN, set1.mG0 + 1, set1.mG1, cutg, whole_r, whole_g, whole_b, + whole_w); + maxb = maximize(set1, BLUE, set1.mB0 + 1, set1.mB1, cutb, whole_r, whole_g, whole_b, + whole_w); + + if (maxr >= maxg && maxr >= maxb) { + dir = RED; + if (cutr[0] < 0) return false; /* can't split the box */ + } else if (maxg >= maxr && maxg >= maxb) { + dir = GREEN; + } else { + dir = BLUE; + } + + set2.mR1 = set1.mR1; + set2.mG1 = set1.mG1; + set2.mB1 = set1.mB1; + + switch (dir) { + case RED: + set2.mR0 = set1.mR1 = cutr[0]; + set2.mG0 = set1.mG0; + set2.mB0 = set1.mB0; + break; + case GREEN: + set2.mG0 = set1.mG1 = cutg[0]; + set2.mR0 = set1.mR0; + set2.mB0 = set1.mB0; + break; + case BLUE: + set2.mB0 = set1.mB1 = cutb[0]; + set2.mR0 = set1.mR0; + set2.mG0 = set1.mG0; + break; + } + set1.mVol = (set1.mR1 - set1.mR0) * (set1.mG1 - set1.mG0) * (set1.mB1 - set1.mB0); + set2.mVol = (set2.mR1 - set2.mR0) * (set2.mG1 - set2.mG0) * (set2.mB1 - set2.mB0); + + return true; + } +} diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java index e82cc737a395..342456a58091 100644 --- a/core/java/com/android/internal/jank/FrameTracker.java +++ b/core/java/com/android/internal/jank/FrameTracker.java @@ -25,6 +25,9 @@ import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_GPU_DEADL import static android.view.SurfaceControl.JankData.PREDICTION_ERROR; import static android.view.SurfaceControl.JankData.SURFACE_FLINGER_SCHEDULING; +import static com.android.internal.jank.InteractionJankMonitor.ACTION_METRICS_LOGGED; +import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_BEGIN; + import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.HardwareRendererObserver; @@ -72,6 +75,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener private long mEndVsyncId = INVALID_ID; private boolean mMetricsFinalized; private boolean mCancelled = false; + private FrameTrackerListener mListener; private static class JankInfo { long frameVsyncId; @@ -109,7 +113,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener @NonNull SurfaceControlWrapper surfaceControlWrapper, @NonNull ChoreographerWrapper choreographer, @NonNull FrameMetricsWrapper metrics, int traceThresholdMissedFrames, - int traceThresholdFrameTimeMillis) { + int traceThresholdFrameTimeMillis, @Nullable FrameTrackerListener listener) { mSession = session; mRendererWrapper = renderer; mMetricsWrapper = metrics; @@ -120,6 +124,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener mObserver = new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), handler); mTraceThresholdMissedFrames = traceThresholdMissedFrames; mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis; + mListener = listener; // If the surface isn't valid yet, wait until it's created. if (viewRootWrapper.getSurfaceControl().isValid()) { @@ -165,11 +170,15 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener */ public synchronized void begin() { mBeginVsyncId = mChoreographer.getVsyncId() + 1; + mSession.setTimeStamp(System.nanoTime()); Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId); mRendererWrapper.addObserver(mObserver); if (mSurfaceControl != null) { mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl); } + if (mListener != null) { + mListener.onNotifyCujEvents(mSession, ACTION_SESSION_BEGIN); + } } /** @@ -224,7 +233,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener } private boolean isInRange(long vsyncId) { - // It's possible that we may miss a callback for the frame with vsyncId == mEndVsyncId. // Because of that, we collect all frames even if they happen after the end so we eventually // have a frame after the end with both callbacks present. @@ -371,6 +379,9 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener missedAppFramesCount + missedSfFramesCounts, maxFrameTimeNanos, missedSfFramesCounts); + if (mListener != null) { + mListener.onNotifyCujEvents(mSession, ACTION_METRICS_LOGGED); + } } if (DEBUG) { Log.i(TAG, "FrameTracker: CUJ=" + mSession.getName() @@ -495,4 +506,17 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener return mChoreographer.getVsyncId(); } } + + /** + * A listener that notifies cuj events. + */ + public interface FrameTrackerListener { + /** + * Notify that the CUJ session was created. + * + * @param session the CUJ session + * @param action the specific action + */ + void onNotifyCujEvents(Session session, String action); + } } diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index cba6af98a980..0294ec398484 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -16,6 +16,8 @@ package com.android.internal.jank; +import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY; + import static com.android.internal.jank.FrameTracker.ChoreographerWrapper; import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL; @@ -48,9 +50,12 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN import android.annotation.IntDef; import android.annotation.NonNull; +import android.content.Context; +import android.content.Intent; import android.os.Build; import android.os.HandlerExecutor; import android.os.HandlerThread; +import android.os.SystemProperties; import android.provider.DeviceConfig; import android.util.Log; import android.util.SparseArray; @@ -59,6 +64,7 @@ import android.view.View; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.FrameTracker.FrameMetricsWrapper; +import com.android.internal.jank.FrameTracker.FrameTrackerListener; import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper; import com.android.internal.jank.FrameTracker.ViewRootWrapper; import com.android.internal.util.PerfettoTrigger; @@ -74,6 +80,8 @@ import java.util.concurrent.TimeUnit; */ public class InteractionJankMonitor { private static final String TAG = InteractionJankMonitor.class.getSimpleName(); + private static final String ACTION_PREFIX = InteractionJankMonitor.class.getCanonicalName(); + private static final String DEFAULT_WORKER_NAME = TAG + "-Worker"; private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5L); private static final String SETTINGS_ENABLED_KEY = "enabled"; @@ -90,6 +98,14 @@ public class InteractionJankMonitor { private static final int DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES = 3; private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64; + public static final String ACTION_SESSION_BEGIN = ACTION_PREFIX + ".ACTION_SESSION_BEGIN"; + public static final String ACTION_SESSION_END = ACTION_PREFIX + ".ACTION_SESSION_END"; + public static final String ACTION_METRICS_LOGGED = ACTION_PREFIX + ".ACTION_METRICS_LOGGED"; + public static final String BUNDLE_KEY_CUJ_NAME = ACTION_PREFIX + ".CUJ_NAME"; + public static final String BUNDLE_KEY_TIMESTAMP = ACTION_PREFIX + ".TIMESTAMP"; + @VisibleForTesting + public static final String PROP_NOTIFY_CUJ_EVENT = "debug.notify_cuj_events"; + // Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE. public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0; public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK = 1; @@ -256,15 +272,28 @@ public class InteractionJankMonitor { */ @VisibleForTesting public FrameTracker createFrameTracker(View v, Session session) { + final Context c = v.getContext().getApplicationContext(); synchronized (this) { + boolean needListener = SystemProperties.getBoolean(PROP_NOTIFY_CUJ_EVENT, false); + FrameTrackerListener eventsListener = + !needListener ? null : (s, act) -> notifyEvents(c, act, s); + return new FrameTracker(session, mWorker.getThreadHandler(), new ThreadedRendererWrapper(v.getThreadedRenderer()), new ViewRootWrapper(v.getViewRootImpl()), new SurfaceControlWrapper(), new ChoreographerWrapper(Choreographer.getInstance()), mMetrics, - mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis); + mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis, eventsListener); } } + private void notifyEvents(Context context, String action, Session session) { + Intent intent = new Intent(action); + intent.putExtra(BUNDLE_KEY_CUJ_NAME, getNameOfCuj(session.getCuj())); + intent.putExtra(BUNDLE_KEY_TIMESTAMP, session.getTimeStamp()); + intent.addFlags(FLAG_RECEIVER_REGISTERED_ONLY); + context.sendBroadcast(intent); + } + /** * Begin a trace session. * @@ -479,6 +508,7 @@ public class InteractionJankMonitor { public static class Session { @CujType private int mCujType; + private long mTimeStamp; public Session(@CujType int cujType) { mCujType = cujType; @@ -505,5 +535,13 @@ public class InteractionJankMonitor { public String getName() { return "J<" + getNameOfCuj(mCujType) + ">"; } + + public void setTimeStamp(long timeStamp) { + mTimeStamp = timeStamp; + } + + public long getTimeStamp() { + return mTimeStamp; + } } } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 1f8ffe08a257..54095bd86887 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -12520,6 +12520,7 @@ public class BatteryStatsImpl extends BatteryStats { * @param totalEnergyUJ energy (microjoules) used for this bucket since this was last called. * @param uidEnergies map of uid->energy (microjoules) for this bucket since last called. * Data inside uidEnergies will not be modified (treated immutable). + * Uids not already known to BatteryStats will be ignored. */ public void updateCustomMeasuredEnergyDataLocked(int customEnergyBucket, long totalEnergyUJ, @Nullable SparseLongArray uidEnergies) { @@ -12540,10 +12541,20 @@ public class BatteryStatsImpl extends BatteryStats { final int uidInt = mapUid(uidEnergies.keyAt(i)); final long uidEnergyUJ = uidEnergies.valueAt(i); if (uidEnergyUJ == 0) continue; - // TODO(b/180030409): Worry about dead Uids (no longer in BSI) being revived by this, - // or converse problem of not creating a new Uid if its first blame is recorded here. - final Uid uidObj = getUidStatsLocked(uidInt); - uidObj.addEnergyToCustomBucketLocked(uidEnergyUJ, customEnergyBucket, true); + final Uid uidObj = getAvailableUidStatsLocked(uidInt); + if (uidObj != null) { + uidObj.addEnergyToCustomBucketLocked(uidEnergyUJ, customEnergyBucket, true); + } else { + // Ignore any uid not already known to BatteryStats, rather than creating a new Uid. + // Otherwise we could end up reviving dead Uids. Note that the CPU data is updated + // first, so any uid that has used any CPU should already be known to BatteryStats. + // Recently removed uids (especially common for isolated uids) can reach this path + // and are ignored. + if (!Process.isIsolated(uidInt)) { + Slog.w(TAG, "Received measured energy " + totalEnergyUJ + " for custom bucket " + + customEnergyBucket + " for non-existent uid " + uidInt); + } + } } } diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index c8afea9b0982..b99c953fa4e8 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -103,7 +103,7 @@ public final class Zygote { */ public static final int PROFILE_FROM_SHELL = 1 << 15; - /** + /* * Enable using the ART app image startup cache */ public static final int USE_APP_IMAGE_STARTUP_CACHE = 1 << 16; @@ -116,6 +116,13 @@ public final class Zygote { */ public static final int DEBUG_IGNORE_APP_SIGNAL_HANDLER = 1 << 17; + /** + * Disable runtime access to {@link android.annotation.TestApi} annotated members. + * + * <p>This only takes effect if Hidden API access restrictions are enabled as well. + */ + public static final int DISABLE_TEST_API_ENFORCEMENT_POLICY = 1 << 18; + public static final int MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20); public static final int MEMORY_TAG_LEVEL_NONE = 0; diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java index 56b25b2060ea..93ba0372df08 100644 --- a/core/java/com/android/internal/policy/TransitionAnimation.java +++ b/core/java/com/android/internal/policy/TransitionAnimation.java @@ -180,6 +180,7 @@ public class TransitionAnimation { "android", com.android.internal.R.anim.cross_profile_apps_thumbnail_enter); } + /** Load animation by resource Id from specific LayoutParams. */ @Nullable private Animation loadAnimationRes(LayoutParams lp, int resId) { Context context = mContext; @@ -193,6 +194,7 @@ public class TransitionAnimation { return null; } + /** Load animation by resource Id from specific package. */ @Nullable private Animation loadAnimationRes(String packageName, int resId) { if (ResourceId.isValid(resId)) { @@ -204,6 +206,13 @@ public class TransitionAnimation { return null; } + /** Load animation by resource Id from android package. */ + @Nullable + public Animation loadDefaultAnimationRes(int resId) { + return loadAnimationRes("android", resId); + } + + /** Load animation by attribute Id from specific LayoutParams */ @Nullable public Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) { int resId = Resources.ID_NULL; @@ -222,6 +231,25 @@ public class TransitionAnimation { return null; } + /** Load animation by attribute Id from android package. */ + @Nullable + public Animation loadDefaultAnimationAttr(int animAttr) { + int resId = Resources.ID_NULL; + Context context = mContext; + if (animAttr >= 0) { + AttributeCache.Entry ent = getCachedAnimations("android", + mDefaultWindowAnimationStyleResId); + if (ent != null) { + context = ent.context; + resId = ent.array.getResourceId(animAttr, 0); + } + } + if (ResourceId.isValid(resId)) { + return loadAnimationSafely(context, resId, mTag); + } + return null; + } + @Nullable private AttributeCache.Entry getCachedAnimations(LayoutParams lp) { if (mDebug) { diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index 7edc6c855ec2..fde48e86b0f3 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -67,7 +67,7 @@ interface IStatusBarService void onNotificationError(String pkg, String tag, int id, int uid, int initialPid, String message, int userId); void onClearAllNotifications(int userId); - void onNotificationClear(String pkg, String tag, int id, int userId, String key, + void onNotificationClear(String pkg, int userId, String key, int dismissalSurface, int dismissalSentiment, in NotificationVisibility nv); void onNotificationVisibilityChanged( in NotificationVisibility[] newlyVisibleKeys, in NotificationVisibility[] noLongerVisibleKeys); diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl index c33637353984..8d82e33dc29f 100644 --- a/core/java/com/android/internal/view/IInputMethod.aidl +++ b/core/java/com/android/internal/view/IInputMethod.aidl @@ -35,7 +35,8 @@ import com.android.internal.view.InlineSuggestionsRequestInfo; * {@hide} */ oneway interface IInputMethod { - void initializeInternal(IBinder token, int displayId, IInputMethodPrivilegedOperations privOps); + void initializeInternal(IBinder token, int displayId, IInputMethodPrivilegedOperations privOps, + int configChanges); void onCreateInlineSuggestionsRequest(in InlineSuggestionsRequestInfo requestInfo, in IInlineSuggestionsRequestCallback cb); diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index 8982519eff96..671cb8481dd9 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -1234,10 +1234,10 @@ public class SystemConfig { addFeature(PackageManager.FEATURE_RAM_NORMAL, 0); } - if (IncrementalManager.isFeatureEnabled()) { + final int incrementalVersion = IncrementalManager.getVersion(); + if (incrementalVersion > 0) { addFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY, 0); - addFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY_VERSION, - IncrementalManager.isV2Available() ? 2 : 1); + addFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY_VERSION, incrementalVersion); } if (PackageManager.APP_ENUMERATION_ENABLED_BY_DEFAULT) { diff --git a/core/jni/Android.bp b/core/jni/Android.bp index cf8711b3c037..b485f0f4fb62 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -121,6 +121,7 @@ cc_library_shared { "android_view_PointerIcon.cpp", "android_view_Surface.cpp", "android_view_SurfaceControl.cpp", + "android_view_SurfaceControlFpsListener.cpp", "android_graphics_BLASTBufferQueue.cpp", "android_view_SurfaceSession.cpp", "android_view_TextureView.cpp", @@ -175,6 +176,7 @@ cc_library_shared { "android_hardware_Camera.cpp", "android_hardware_camera2_CameraMetadata.cpp", "android_hardware_camera2_DngCreator.cpp", + "android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp", "android_hardware_camera2_utils_SurfaceUtils.cpp", "android_hardware_display_DisplayManagerGlobal.cpp", "android_hardware_display_DisplayViewport.cpp", @@ -231,6 +233,7 @@ cc_library_shared { "audioclient-types-aidl-cpp", "audioflinger-aidl-cpp", "av-types-aidl-cpp", + "android.hardware.camera.device@3.2", "libandroidicu", "libbpf_android", "libnetdbpf", @@ -260,6 +263,7 @@ cc_library_shared { "libdataloader", "libvulkan", "libETC1", + "libjpeg", "libhardware", "libhardware_legacy", "libselinux", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 38bcc0f4c59e..dc77bba44607 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -74,6 +74,7 @@ extern int register_android_opengl_jni_GLES32(JNIEnv* env); extern int register_android_hardware_Camera(JNIEnv *env); extern int register_android_hardware_camera2_CameraMetadata(JNIEnv *env); extern int register_android_hardware_camera2_DngCreator(JNIEnv *env); +extern int register_android_hardware_camera2_impl_CameraExtensionJpegProcessor(JNIEnv* env); extern int register_android_hardware_camera2_utils_SurfaceUtils(JNIEnv* env); extern int register_android_hardware_display_DisplayManagerGlobal(JNIEnv* env); extern int register_android_hardware_HardwareBuffer(JNIEnv *env); @@ -119,6 +120,7 @@ extern int register_android_view_InputApplicationHandle(JNIEnv* env); extern int register_android_view_InputWindowHandle(JNIEnv* env); extern int register_android_view_Surface(JNIEnv* env); extern int register_android_view_SurfaceControl(JNIEnv* env); +extern int register_android_view_SurfaceControlFpsListener(JNIEnv* env); extern int register_android_view_SurfaceSession(JNIEnv* env); extern int register_android_view_CompositionSamplingListener(JNIEnv* env); extern int register_android_view_TextureView(JNIEnv* env); @@ -1487,6 +1489,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_view_InputWindowHandle), REG_JNI(register_android_view_Surface), REG_JNI(register_android_view_SurfaceControl), + REG_JNI(register_android_view_SurfaceControlFpsListener), REG_JNI(register_android_view_SurfaceSession), REG_JNI(register_android_view_CompositionSamplingListener), REG_JNI(register_android_view_TextureView), @@ -1533,6 +1536,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_hardware_Camera), REG_JNI(register_android_hardware_camera2_CameraMetadata), REG_JNI(register_android_hardware_camera2_DngCreator), + REG_JNI(register_android_hardware_camera2_impl_CameraExtensionJpegProcessor), REG_JNI(register_android_hardware_camera2_utils_SurfaceUtils), REG_JNI(register_android_hardware_display_DisplayManagerGlobal), REG_JNI(register_android_hardware_HardwareBuffer), diff --git a/core/jni/android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp b/core/jni/android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp new file mode 100644 index 000000000000..139075907bf3 --- /dev/null +++ b/core/jni/android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp @@ -0,0 +1,629 @@ +/* + * 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. + */ + +#include <array> +#include <cstring> +#include <cstdio> +#include <inttypes.h> +#include <memory.h> +#include <vector> + +#include <setjmp.h> + +#include <android/hardware/camera/device/3.2/types.h> + +#include "core_jni_helpers.h" +#include "jni.h" +#include <nativehelper/JNIHelp.h> + +#define CAMERA_PROCESSOR_CLASS_NAME "android/hardware/camera2/impl/CameraExtensionJpegProcessor" + +extern "C" { +#include "jpeglib.h" +} + +using namespace std; +using namespace android; + +using android::hardware::camera::device::V3_2::CameraBlob; +using android::hardware::camera::device::V3_2::CameraBlobId; + +class Transform; +struct Plane; + +inline int sgn(int val) { return (0 < val) - (val < 0); } + +inline int min(int a, int b) { return a < b ? a : b; } + +inline int max(int a, int b) { return a > b ? a : b; } + +/** + * Represents a combined cropping and rotation transformation. + * + * The transformation maps the coordinates (mOrigX, mOrigY) and (mOneX, mOneY) + * in the input image to the origin and (mOutputWidth, mOutputHeight) + * respectively. + */ +class Transform { + public: + Transform(int origX, int origY, int oneX, int oneY); + + static Transform forCropFollowedByRotation(int cropLeft, int cropTop, + int cropRight, int cropBottom, int rot90); + + inline int getOutputWidth() const { return mOutputWidth; } + + inline int getOutputHeight() const { return mOutputHeight; } + + bool operator==(const Transform& other) const; + + /** + * Transforms the input coordinates. Coordinates outside the cropped region + * are clamped to valid values. + */ + void map(int x, int y, int* outX, int* outY) const; + + private: + int mOutputWidth; + int mOutputHeight; + + // The coordinates of the point to map the origin to. + const int mOrigX, mOrigY; + // The coordinates of the point to map the point (getOutputWidth(), + // getOutputHeight()) to. + const int mOneX, mOneY; + + // A matrix for the rotational component. + int mMat00, mMat01; + int mMat10, mMat11; +}; + +/** + * Represents a model for accessing pixel data for a single plane of an image. + * Note that the actual data is not owned by this class, and the underlying + * data does not need to be stored in separate planes. + */ +struct Plane { + // The dimensions of this plane of the image + int width; + int height; + + // A pointer to raw pixel data + const unsigned char* data; + // The difference in address between consecutive pixels in the same row + int pixelStride; + // The difference in address between the start of consecutive rows + int rowStride; +}; + +/** + * Provides an interface for simultaneously reading a certain number of rows of + * an image plane as contiguous arrays, suitable for use with libjpeg. + */ +template <unsigned int ROWS> +class RowIterator { + public: + /** + * Creates a new RowIterator which will crop and rotate with the given + * transform. + * + * @param plane the plane to iterate over + * @param transform the transformation to map output values into the + * coordinate space of the plane + * @param rowLength the length of the rows returned via LoadAt(). If this is + * longer than the width of the output (after applying the transform), then + * the right-most value is repeated. + */ + inline RowIterator(Plane plane, Transform transform, int rowLength); + + /** + * Returns an array of pointers into consecutive rows of contiguous image + * data starting at y. That is, samples within each row are contiguous. + * However, the individual arrays pointed-to may be separate. + * When the end of the image is reached, the last row of the image is + * repeated. + * The returned pointers are valid until the next call to loadAt(). + */ + inline const std::array<unsigned char*, ROWS> loadAt(int baseY); + + private: + Plane mPlane; + Transform mTransform; + // The length of a row, with padding to the next multiple of 64. + int mPaddedRowLength; + std::vector<unsigned char> mBuffer; +}; + +template <unsigned int ROWS> +RowIterator<ROWS>::RowIterator(Plane plane, Transform transform, + int rowLength) + : mPlane(plane), mTransform(transform) { + mPaddedRowLength = rowLength; + mBuffer = std::vector<unsigned char>(rowLength * ROWS); +} + +template <unsigned int ROWS> +const std::array<unsigned char*, ROWS> RowIterator<ROWS>::loadAt(int baseY) { + std::array<unsigned char*, ROWS> bufPtrs; + for (unsigned int i = 0; i < ROWS; i++) { + bufPtrs[i] = &mBuffer[mPaddedRowLength * i]; + } + + if (mPlane.width == 0 || mPlane.height == 0) { + return bufPtrs; + } + + for (unsigned int i = 0; i < ROWS; i++) { + int y = i + baseY; + y = min(y, mTransform.getOutputHeight() - 1); + + int output_width = mPaddedRowLength; + output_width = min(output_width, mTransform.getOutputWidth()); + output_width = min(output_width, mPlane.width); + + // Each row in the output image will be copied into buf_ by gathering pixels + // along an axis-aligned line in the plane. + // The line is defined by (startX, startY) -> (endX, endY), computed via the + // current Transform. + int startX; + int startY; + mTransform.map(0, y, &startX, &startY); + + int endX; + int endY; + mTransform.map(output_width - 1, y, &endX, &endY); + + // Clamp (startX, startY) and (endX, endY) to the valid bounds of the plane. + startX = min(startX, mPlane.width - 1); + startY = min(startY, mPlane.height - 1); + endX = min(endX, mPlane.width - 1); + endY = min(endY, mPlane.height - 1); + startX = max(startX, 0); + startY = max(startY, 0); + endX = max(endX, 0); + endY = max(endY, 0); + + // To reduce work inside the copy-loop, precompute the start, end, and + // stride relating the values to be gathered from mPlane into buf + // for this particular scan-line. + int dx = sgn(endX - startX); + int dy = sgn(endY - startY); + if (!(dx == 0 || dy == 0)) { + ALOGE("%s: Unexpected bounds: %dx%d %dx%d!", __FUNCTION__, startX, endX, startY, endY); + return bufPtrs; + } + + // The index into mPlane.data of (startX, startY) + int plane_start = startX * mPlane.pixelStride + startY * mPlane.rowStride; + // The index into mPlane.data of (endX, endY) + int plane_end = endX * mPlane.pixelStride + endY * mPlane.rowStride; + // The stride, in terms of indices in plane_data, required to enumerate the + // samples between the start and end points. + int stride = dx * mPlane.pixelStride + dy * mPlane.rowStride; + // In the degenerate-case of a 1x1 plane, startX and endX are equal, so + // stride would be 0, resulting in an infinite-loop. To avoid this case, + // use a stride of at-least 1. + if (stride == 0) { + stride = 1; + } + + int outX = 0; + for (int idx = plane_start; idx >= min(plane_start, plane_end) && + idx <= max(plane_start, plane_end); idx += stride) { + bufPtrs[i][outX] = mPlane.data[idx]; + outX++; + } + + // Fill the remaining right-edge of the buffer by extending the last + // value. + unsigned char right_padding_value = bufPtrs[i][outX - 1]; + for (; outX < mPaddedRowLength; outX++) { + bufPtrs[i][outX] = right_padding_value; + } + } + + return bufPtrs; +} + +template <typename T> +void safeDelete(T& t) { + delete t; + t = nullptr; +} + +template <typename T> +void safeDeleteArray(T& t) { + delete[] t; + t = nullptr; +} + +Transform::Transform(int origX, int origY, int oneX, int oneY) + : mOrigX(origX), mOrigY(origY), mOneX(oneX), mOneY(oneY) { + if (origX == oneX || origY == oneY) { + // Handle the degenerate case of cropping to a 0x0 rectangle. + mMat00 = 0; + mMat01 = 0; + mMat10 = 0; + mMat11 = 0; + return; + } + + if (oneX > origX && oneY > origY) { + // 0-degree rotation + mMat00 = 1; + mMat01 = 0; + mMat10 = 0; + mMat11 = 1; + mOutputWidth = abs(oneX - origX); + mOutputHeight = abs(oneY - origY); + } else if (oneX < origX && oneY > origY) { + // 90-degree CCW rotation + mMat00 = 0; + mMat01 = -1; + mMat10 = 1; + mMat11 = 0; + mOutputWidth = abs(oneY - origY); + mOutputHeight = abs(oneX - origX); + } else if (oneX > origX && oneY < origY) { + // 270-degree CCW rotation + mMat00 = 0; + mMat01 = 1; + mMat10 = -1; + mMat11 = 0; + mOutputWidth = abs(oneY - origY); + mOutputHeight = abs(oneX - origX);; + } else if (oneX < origX && oneY < origY) { + // 180-degree CCW rotation + mMat00 = -1; + mMat01 = 0; + mMat10 = 0; + mMat11 = -1; + mOutputWidth = abs(oneX - origX); + mOutputHeight = abs(oneY - origY); + } +} + +Transform Transform::forCropFollowedByRotation(int cropLeft, int cropTop, int cropRight, + int cropBottom, int rot90) { + // The input crop-region excludes cropRight and cropBottom, so transform the + // crop rect such that it defines the entire valid region of pixels + // inclusively. + cropRight -= 1; + cropBottom -= 1; + + int cropXLow = min(cropLeft, cropRight); + int cropYLow = min(cropTop, cropBottom); + int cropXHigh = max(cropLeft, cropRight); + int cropYHigh = max(cropTop, cropBottom); + rot90 %= 4; + if (rot90 == 0) { + return Transform(cropXLow, cropYLow, cropXHigh + 1, cropYHigh + 1); + } else if (rot90 == 1) { + return Transform(cropXHigh, cropYLow, cropXLow - 1, cropYHigh + 1); + } else if (rot90 == 2) { + return Transform(cropXHigh, cropYHigh, cropXLow - 1, cropYLow - 1); + } else if (rot90 == 3) { + return Transform(cropXLow, cropYHigh, cropXHigh + 1, cropYLow - 1); + } + // Impossible case. + return Transform(cropXLow, cropYLow, cropXHigh + 1, cropYHigh + 1); +} + +bool Transform::operator==(const Transform& other) const { + return other.mOrigX == mOrigX && // + other.mOrigY == mOrigY && // + other.mOneX == mOneX && // + other.mOneY == mOneY; +} + +/** + * Transforms the input coordinates. Coordinates outside the cropped region + * are clamped to valid values. + */ +void Transform::map(int x, int y, int* outX, int* outY) const { + x = max(x, 0); + y = max(y, 0); + x = min(x, getOutputWidth() - 1); + y = min(y, getOutputHeight() - 1); + *outX = x * mMat00 + y * mMat01 + mOrigX; + *outY = x * mMat10 + y * mMat11 + mOrigY; +} + +int compress(int img_width, int img_height, RowIterator<16>& y_row_generator, + RowIterator<8>& cb_row_generator, RowIterator<8>& cr_row_generator, + unsigned char* out_buf, size_t out_buf_capacity, std::function<void(size_t)> flush, + int quality) { + // libjpeg requires the use of setjmp/longjmp to recover from errors. Since + // this doesn't play well with RAII, we must use pointers and manually call + // delete. See POSIX documentation for longjmp() for details on why the + // volatile keyword is necessary. + volatile jpeg_compress_struct cinfov; + + jpeg_compress_struct& cinfo = + *const_cast<struct jpeg_compress_struct*>(&cinfov); + + JSAMPROW* volatile yArr = nullptr; + JSAMPROW* volatile cbArr = nullptr; + JSAMPROW* volatile crArr = nullptr; + + JSAMPARRAY imgArr[3]; + + // Error handling + + struct my_error_mgr { + struct jpeg_error_mgr pub; + jmp_buf setjmp_buffer; + } err; + + cinfo.err = jpeg_std_error(&err.pub); + + // Default error_exit will call exit(), so override + // to return control via setjmp/longjmp. + err.pub.error_exit = [](j_common_ptr cinfo) { + my_error_mgr* myerr = reinterpret_cast<my_error_mgr*>(cinfo->err); + + (*cinfo->err->output_message)(cinfo); + + // Return control to the setjmp point (see call to setjmp()). + longjmp(myerr->setjmp_buffer, 1); + }; + + cinfo.err = (struct jpeg_error_mgr*)&err; + + // Set the setjmp point to return to in case of error. + if (setjmp(err.setjmp_buffer)) { + // If libjpeg hits an error, control will jump to this point (see call to + // longjmp()). + jpeg_destroy_compress(&cinfo); + + safeDeleteArray(yArr); + safeDeleteArray(cbArr); + safeDeleteArray(crArr); + + return -1; + } + + // Create jpeg compression context + jpeg_create_compress(&cinfo); + + // Stores data needed by our c-style callbacks into libjpeg + struct ClientData { + unsigned char* out_buf; + size_t out_buf_capacity; + std::function<void(size_t)> flush; + int totalOutputBytes; + } clientData{out_buf, out_buf_capacity, flush, 0}; + + cinfo.client_data = &clientData; + + // Initialize destination manager + jpeg_destination_mgr dest; + + dest.init_destination = [](j_compress_ptr cinfo) { + ClientData& cdata = *reinterpret_cast<ClientData*>(cinfo->client_data); + + cinfo->dest->next_output_byte = cdata.out_buf; + cinfo->dest->free_in_buffer = cdata.out_buf_capacity; + }; + + dest.empty_output_buffer = [](j_compress_ptr cinfo) -> boolean { + ClientData& cdata = *reinterpret_cast<ClientData*>(cinfo->client_data); + + size_t numBytesInBuffer = cdata.out_buf_capacity; + cdata.flush(numBytesInBuffer); + cdata.totalOutputBytes += numBytesInBuffer; + + // Reset the buffer + cinfo->dest->next_output_byte = cdata.out_buf; + cinfo->dest->free_in_buffer = cdata.out_buf_capacity; + + return true; + }; + + dest.term_destination = [](j_compress_ptr cinfo __unused) { + // do nothing to terminate the output buffer + }; + + cinfo.dest = &dest; + + // Set jpeg parameters + cinfo.image_width = img_width; + cinfo.image_height = img_height; + cinfo.input_components = 3; + + // Set defaults based on the above values + jpeg_set_defaults(&cinfo); + + jpeg_set_quality(&cinfo, quality, true); + + cinfo.dct_method = JDCT_IFAST; + + cinfo.raw_data_in = true; + + jpeg_set_colorspace(&cinfo, JCS_YCbCr); + + cinfo.comp_info[0].h_samp_factor = 2; + cinfo.comp_info[0].v_samp_factor = 2; + cinfo.comp_info[1].h_samp_factor = 1; + cinfo.comp_info[1].v_samp_factor = 1; + cinfo.comp_info[2].h_samp_factor = 1; + cinfo.comp_info[2].v_samp_factor = 1; + + jpeg_start_compress(&cinfo, true); + + yArr = new JSAMPROW[cinfo.comp_info[0].v_samp_factor * DCTSIZE]; + cbArr = new JSAMPROW[cinfo.comp_info[1].v_samp_factor * DCTSIZE]; + crArr = new JSAMPROW[cinfo.comp_info[2].v_samp_factor * DCTSIZE]; + + imgArr[0] = const_cast<JSAMPARRAY>(yArr); + imgArr[1] = const_cast<JSAMPARRAY>(cbArr); + imgArr[2] = const_cast<JSAMPARRAY>(crArr); + + for (int y = 0; y < img_height; y += DCTSIZE * 2) { + std::array<unsigned char*, 16> yData = y_row_generator.loadAt(y); + std::array<unsigned char*, 8> cbData = cb_row_generator.loadAt(y / 2); + std::array<unsigned char*, 8> crData = cr_row_generator.loadAt(y / 2); + + for (int row = 0; row < DCTSIZE * 2; row++) { + yArr[row] = yData[row]; + } + for (int row = 0; row < DCTSIZE; row++) { + cbArr[row] = cbData[row]; + crArr[row] = crData[row]; + } + + jpeg_write_raw_data(&cinfo, imgArr, DCTSIZE * 2); + } + + jpeg_finish_compress(&cinfo); + + int numBytesInBuffer = cinfo.dest->next_output_byte - out_buf; + + flush(numBytesInBuffer); + + clientData.totalOutputBytes += numBytesInBuffer; + + safeDeleteArray(yArr); + safeDeleteArray(cbArr); + safeDeleteArray(crArr); + + jpeg_destroy_compress(&cinfo); + + return clientData.totalOutputBytes; +} + +int compress( + /** Input image dimensions */ + int width, int height, + /** Y Plane */ + unsigned char* yBuf, int yPStride, int yRStride, + /** Cb Plane */ + unsigned char* cbBuf, int cbPStride, int cbRStride, + /** Cr Plane */ + unsigned char* crBuf, int crPStride, int crRStride, + /** Output */ + unsigned char* outBuf, size_t outBufCapacity, + /** Jpeg compression parameters */ + int quality, + /** Crop */ + int cropLeft, int cropTop, int cropRight, int cropBottom, + /** Rotation (multiple of 90). For example, rot90 = 1 implies a 90 degree + * rotation. */ + int rot90) { + int finalWidth; + int finalHeight; + finalWidth = cropRight - cropLeft; + finalHeight = cropBottom - cropTop; + + rot90 %= 4; + // for 90 and 270-degree rotations, flip the final width and height + if (rot90 == 1) { + finalWidth = cropBottom - cropTop; + finalHeight = cropRight - cropLeft; + } else if (rot90 == 3) { + finalWidth = cropBottom - cropTop; + finalHeight = cropRight - cropLeft; + } + + const Plane yP = {width, height, yBuf, yPStride, yRStride}; + const Plane cbP = {width / 2, height / 2, cbBuf, cbPStride, cbRStride}; + const Plane crP = {width / 2, height / 2, crBuf, crPStride, crRStride}; + + auto flush = [](size_t numBytes __unused) { + // do nothing + }; + + // Round up to the nearest multiple of 64. + int y_row_length = (finalWidth + 16 + 63) & ~63; + int cb_row_length = (finalWidth / 2 + 16 + 63) & ~63; + int cr_row_length = (finalWidth / 2 + 16 + 63) & ~63; + + Transform yTrans = Transform::forCropFollowedByRotation( + cropLeft, cropTop, cropRight, cropBottom, rot90); + + Transform chromaTrans = Transform::forCropFollowedByRotation( + cropLeft / 2, cropTop / 2, cropRight / 2, cropBottom / 2, rot90); + + RowIterator<16> yIter(yP, yTrans, y_row_length); + RowIterator<8> cbIter(cbP, chromaTrans, cb_row_length); + RowIterator<8> crIter(crP, chromaTrans, cr_row_length); + + return compress(finalWidth, finalHeight, yIter, cbIter, crIter, outBuf, outBufCapacity, flush, + quality); +} + +extern "C" { + +static jint CameraExtensionJpegProcessor_compressJpegFromYUV420p( + JNIEnv* env, jclass clazz __unused, + /** Input image dimensions */ + jint width, jint height, + /** Y Plane */ + jobject yBuf, jint yPStride, jint yRStride, + /** Cb Plane */ + jobject cbBuf, jint cbPStride, jint cbRStride, + /** Cr Plane */ + jobject crBuf, jint crPStride, jint crRStride, + /** Output */ + jobject outBuf, jint outBufCapacity, + /** Jpeg compression parameters */ + jint quality, + /** Crop */ + jint cropLeft, jint cropTop, jint cropRight, jint cropBottom, + /** Rotation (multiple of 90). For example, rot90 = 1 implies a 90 degree + * rotation. */ + jint rot90) { + jbyte* y = (jbyte*)env->GetDirectBufferAddress(yBuf); + jbyte* cb = (jbyte*)env->GetDirectBufferAddress(cbBuf); + jbyte* cr = (jbyte*)env->GetDirectBufferAddress(crBuf); + jbyte* out = (jbyte*)env->GetDirectBufferAddress(outBuf); + + size_t actualJpegSize = compress(width, height, + (unsigned char*)y, yPStride, yRStride, + (unsigned char*)cb, cbPStride, cbRStride, + (unsigned char*)cr, crPStride, crRStride, + (unsigned char*)out, (size_t)outBufCapacity, + quality, cropLeft, cropTop, cropRight, cropBottom, rot90); + + size_t finalJpegSize = actualJpegSize + sizeof(CameraBlob); + if (finalJpegSize > outBufCapacity) { + ALOGE("%s: Final jpeg buffer %zu not large enough for the jpeg blob header with "\ + "capacity %d", __FUNCTION__, finalJpegSize, outBufCapacity); + return actualJpegSize; + } + + int8_t* header = static_cast<int8_t *> (out) + + (outBufCapacity - sizeof(CameraBlob)); + CameraBlob *blob = reinterpret_cast<CameraBlob *> (header); + blob->blobId = CameraBlobId::JPEG; + blob->blobSize = actualJpegSize; + + return actualJpegSize; +} + +} // extern "C" + +static const JNINativeMethod gCameraExtensionJpegProcessorMethods[] = { + {"compressJpegFromYUV420pNative", + "(IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIIIIII)I", + (void*)CameraExtensionJpegProcessor_compressJpegFromYUV420p}}; + +// Get all the required offsets in java class and register native functions +int register_android_hardware_camera2_impl_CameraExtensionJpegProcessor(JNIEnv* env) { + // Register native functions + return RegisterMethodsOrDie(env, CAMERA_PROCESSOR_CLASS_NAME, + gCameraExtensionJpegProcessorMethods, NELEM(gCameraExtensionJpegProcessorMethods)); +} diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index d11ee3a875aa..451ea93349f7 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -27,7 +27,6 @@ #include <android-base/chrono_utils.h> #include <android/graphics/region.h> #include <android/gui/BnScreenCaptureListener.h> -#include <android/hardware/display/IDeviceProductInfoConstants.h> #include <android/os/IInputConstants.h> #include <android_runtime/AndroidRuntime.h> #include <android_runtime/android_hardware_HardwareBuffer.h> @@ -98,6 +97,8 @@ static struct { jfieldID supportedColorModes; jfieldID activeColorMode; jfieldID hdrCapabilities; + jfieldID autoLowLatencyModeSupported; + jfieldID gameContentTypeSupported; } gDynamicDisplayInfoClassInfo; static struct { @@ -1021,24 +1022,16 @@ static jobject convertDeviceProductInfoToJavaObject( } else { LOG_FATAL("Unknown alternative for variant DeviceProductInfo::ManufactureOrModelDate"); } - jint connectionToSinkType; - // Relative address maps to HDMI physical address. All addresses are 4 digits long allowing - // for a 5–device-deep hierarchy. For more information, refer: - // Section 8.7 - Physical Address of HDMI Specification Version 1.3a - using android::hardware::display::IDeviceProductInfoConstants; - if (info->relativeAddress.size() != 4) { - connectionToSinkType = IDeviceProductInfoConstants::CONNECTION_TO_SINK_UNKNOWN; - } else if (info->relativeAddress[0] == 0) { - connectionToSinkType = IDeviceProductInfoConstants::CONNECTION_TO_SINK_BUILT_IN; - } else if (info->relativeAddress[1] == 0) { - connectionToSinkType = IDeviceProductInfoConstants::CONNECTION_TO_SINK_DIRECT; - } else { - connectionToSinkType = IDeviceProductInfoConstants::CONNECTION_TO_SINK_TRANSITIVE; + auto relativeAddress = env->NewIntArray(info->relativeAddress.size()); + auto relativeAddressData = env->GetIntArrayElements(relativeAddress, nullptr); + for (int i = 0; i < info->relativeAddress.size(); i++) { + relativeAddressData[i] = info->relativeAddress[i]; } + env->ReleaseIntArrayElements(relativeAddress, relativeAddressData, 0); return env->NewObject(gDeviceProductInfoClassInfo.clazz, gDeviceProductInfoClassInfo.ctor, name, manufacturerPnpId, productId, modelYear, manufactureDate, - connectionToSinkType); + relativeAddress); } static jobject nativeGetStaticDisplayInfo(JNIEnv* env, jclass clazz, jobject tokenObj) { @@ -1134,6 +1127,11 @@ static jobject nativeGetDynamicDisplayInfo(JNIEnv* env, jclass clazz, jobject to env->SetObjectField(object, gDynamicDisplayInfoClassInfo.hdrCapabilities, convertDeviceProductInfoToJavaObject(env, info.hdrCapabilities)); + env->SetBooleanField(object, gDynamicDisplayInfoClassInfo.autoLowLatencyModeSupported, + info.autoLowLatencyModeSupported); + + env->SetBooleanField(object, gDynamicDisplayInfoClassInfo.gameContentTypeSupported, + info.gameContentTypeSupported); return object; } @@ -1458,20 +1456,6 @@ static void nativeReparent(JNIEnv* env, jclass clazz, jlong transactionObj, transaction->reparent(ctrl, newParent); } -static jboolean nativeGetAutoLowLatencyModeSupport(JNIEnv* env, jclass clazz, jobject tokenObject) { - sp<IBinder> token(ibinderForJavaObject(env, tokenObject)); - if (token == NULL) return NULL; - - return SurfaceComposerClient::getAutoLowLatencyModeSupport(token); -} - -static jboolean nativeGetGameContentTypeSupport(JNIEnv* env, jclass clazz, jobject tokenObject) { - sp<IBinder> token(ibinderForJavaObject(env, tokenObject)); - if (token == NULL) return NULL; - - return SurfaceComposerClient::getGameContentTypeSupport(token); -} - static void nativeSetAutoLowLatencyMode(JNIEnv* env, jclass clazz, jobject tokenObject, jboolean on) { sp<IBinder> token(ibinderForJavaObject(env, tokenObject)); if (token == NULL) return; @@ -1821,12 +1805,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeGetDisplayNativePrimaries }, {"nativeSetActiveColorMode", "(Landroid/os/IBinder;I)Z", (void*)nativeSetActiveColorMode}, - {"nativeGetAutoLowLatencyModeSupport", "(Landroid/os/IBinder;)Z", - (void*)nativeGetAutoLowLatencyModeSupport }, {"nativeSetAutoLowLatencyMode", "(Landroid/os/IBinder;Z)V", (void*)nativeSetAutoLowLatencyMode }, - {"nativeGetGameContentTypeSupport", "(Landroid/os/IBinder;)Z", - (void*)nativeGetGameContentTypeSupport }, {"nativeSetGameContentType", "(Landroid/os/IBinder;Z)V", (void*)nativeSetGameContentType }, {"nativeGetCompositionDataspaces", "()[I", @@ -1934,6 +1914,10 @@ int register_android_view_SurfaceControl(JNIEnv* env) gDynamicDisplayInfoClassInfo.hdrCapabilities = GetFieldIDOrDie(env, dynamicInfoClazz, "hdrCapabilities", "Landroid/view/Display$HdrCapabilities;"); + gDynamicDisplayInfoClassInfo.autoLowLatencyModeSupported = + GetFieldIDOrDie(env, dynamicInfoClazz, "autoLowLatencyModeSupported", "Z"); + gDynamicDisplayInfoClassInfo.gameContentTypeSupported = + GetFieldIDOrDie(env, dynamicInfoClazz, "gameContentTypeSupported", "Z"); jclass modeClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayMode"); gDisplayModeClassInfo.clazz = MakeGlobalRefOrDie(env, modeClazz); @@ -1986,7 +1970,7 @@ int register_android_view_SurfaceControl(JNIEnv* env) "Ljava/lang/String;" "Ljava/lang/Integer;" "Landroid/hardware/display/DeviceProductInfo$ManufactureDate;" - "I)V"); + "[I)V"); jclass deviceProductInfoManufactureDateClazz = FindClassOrDie(env, "android/hardware/display/DeviceProductInfo$ManufactureDate"); diff --git a/core/jni/android_view_SurfaceControlFpsListener.cpp b/core/jni/android_view_SurfaceControlFpsListener.cpp new file mode 100644 index 000000000000..6fa12e510459 --- /dev/null +++ b/core/jni/android_view_SurfaceControlFpsListener.cpp @@ -0,0 +1,130 @@ +/* + * 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. + */ + +#define LOG_TAG "SurfaceControlFpsListener" + +#include <android/gui/BnFpsListener.h> +#include <android_runtime/AndroidRuntime.h> +#include <android_runtime/Log.h> +#include <gui/ISurfaceComposer.h> +#include <gui/SurfaceComposerClient.h> +#include <nativehelper/JNIHelp.h> +#include <utils/Log.h> +#include <utils/RefBase.h> + +#include "android_util_Binder.h" +#include "core_jni_helpers.h" + +namespace android { + +namespace { + +struct { + jclass mClass; + jmethodID mDispatchOnFpsReported; +} gListenerClassInfo; + +struct SurfaceControlFpsListener : public gui::BnFpsListener { + SurfaceControlFpsListener(JNIEnv* env, jobject listener) + : mListener(env->NewWeakGlobalRef(listener)) {} + + binder::Status onFpsReported(float fps) override { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + LOG_ALWAYS_FATAL_IF(env == nullptr, "Unable to retrieve JNIEnv in onFpsReported."); + + jobject listener = env->NewGlobalRef(mListener); + if (listener == NULL) { + // Weak reference went out of scope + return binder::Status::ok(); + } + env->CallStaticVoidMethod(gListenerClassInfo.mClass, + gListenerClassInfo.mDispatchOnFpsReported, listener, + static_cast<jfloat>(fps)); + env->DeleteGlobalRef(listener); + + if (env->ExceptionCheck()) { + ALOGE("SurfaceControlFpsListener.onFpsReported() failed."); + LOGE_EX(env); + env->ExceptionClear(); + } + return binder::Status::ok(); + } + +protected: + virtual ~SurfaceControlFpsListener() { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->DeleteWeakGlobalRef(mListener); + } + +private: + jweak mListener; +}; + +jlong nativeCreate(JNIEnv* env, jclass clazz, jobject obj) { + SurfaceControlFpsListener* listener = new SurfaceControlFpsListener(env, obj); + listener->incStrong((void*)nativeCreate); + return reinterpret_cast<jlong>(listener); +} + +void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) { + SurfaceControlFpsListener* listener = reinterpret_cast<SurfaceControlFpsListener*>(ptr); + listener->decStrong((void*)nativeCreate); +} + +void nativeRegister(JNIEnv* env, jclass clazz, jlong ptr, jlong layerObj) { + sp<SurfaceControlFpsListener> listener = reinterpret_cast<SurfaceControlFpsListener*>(ptr); + auto layer = reinterpret_cast<SurfaceControl*>(layerObj); + sp<IBinder> layerHandle = layer != nullptr ? layer->getHandle() : nullptr; + if (SurfaceComposerClient::addFpsListener(layerHandle, listener) != OK) { + constexpr auto error_msg = "Couldn't addFpsListener"; + ALOGE(error_msg); + jniThrowRuntimeException(env, error_msg); + } +} + +void nativeUnregister(JNIEnv* env, jclass clazz, jlong ptr) { + sp<SurfaceControlFpsListener> listener = reinterpret_cast<SurfaceControlFpsListener*>(ptr); + + if (SurfaceComposerClient::removeFpsListener(listener) != OK) { + constexpr auto error_msg = "Couldn't removeFpsListener"; + ALOGE(error_msg); + jniThrowRuntimeException(env, error_msg); + } +} + +const JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + {"nativeCreate", "(Landroid/view/SurfaceControlFpsListener;)J", (void*)nativeCreate}, + {"nativeDestroy", "(J)V", (void*)nativeDestroy}, + {"nativeRegister", "(JJ)V", (void*)nativeRegister}, + {"nativeUnregister", "(J)V", (void*)nativeUnregister}}; + +} // namespace + +int register_android_view_SurfaceControlFpsListener(JNIEnv* env) { + int res = jniRegisterNativeMethods(env, "android/view/SurfaceControlFpsListener", gMethods, + NELEM(gMethods)); + LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods."); + + jclass clazz = env->FindClass("android/view/SurfaceControlFpsListener"); + gListenerClassInfo.mClass = MakeGlobalRefOrDie(env, clazz); + gListenerClassInfo.mDispatchOnFpsReported = + env->GetStaticMethodID(clazz, "dispatchOnFpsReported", + "(Landroid/view/SurfaceControlFpsListener;F)V"); + return 0; +} + +} // namespace android diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index c882431c9366..ec502c3c272f 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -174,10 +174,10 @@ message DisplayContentProto { // Use root_display_area instead optional WindowContainerProto window_container = 1 [deprecated=true]; optional int32 id = 2; - reserved 3; // stacks - optional DockedStackDividerControllerProto docked_stack_divider_controller = 4 [deprecated=true]; + reserved 3; // RootTasks + optional DockedTaskDividerControllerProto docked_task_divider_controller = 4 [deprecated=true]; // Will be removed soon. - optional PinnedStackControllerProto pinned_stack_controller = 5 [deprecated=true]; + optional PinnedTaskControllerProto pinned_task_controller = 5 [deprecated=true]; /* non app windows */ repeated WindowTokenProto above_app_windows = 6 [deprecated=true]; repeated WindowTokenProto below_app_windows = 7 [deprecated=true]; @@ -256,15 +256,15 @@ message DisplayRotationProto { optional int32 last_orientation = 5 [(.android.typedef) = "android.content.pm.ActivityInfo.ScreenOrientation"]; } -/* represents DockedStackDividerController */ -message DockedStackDividerControllerProto { +/* represents DockedTaskDividerController */ +message DockedTaskDividerControllerProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; optional bool minimized_dock = 1 [deprecated=true]; } -/* represents PinnedStackController */ -message PinnedStackControllerProto { +/* represents PinnedTaskController */ +message PinnedTaskControllerProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; optional .android.graphics.RectProto default_bounds = 1 [deprecated=true]; @@ -406,6 +406,8 @@ message WindowStateProto { optional int64 finished_seamless_rotation_frame = 40; optional WindowFramesProto window_frames = 41; optional bool force_seamless_rotation = 42; + optional bool in_size_compat_mode = 43; + optional float global_scale = 44; } message IdentifierProto { @@ -516,6 +518,7 @@ message WindowFramesProto { optional .android.graphics.RectProto visible_insets = 13 [deprecated=true]; optional .android.graphics.RectProto stable_insets = 14 [deprecated=true]; optional .android.graphics.RectProto outsets = 15; + optional .android.graphics.RectProto compat_frame = 16; } message InsetsSourceProviderProto { diff --git a/core/res/Android.bp b/core/res/Android.bp index 4cf9cfb7120a..b988b5a92af7 100644 --- a/core/res/Android.bp +++ b/core/res/Android.bp @@ -44,11 +44,73 @@ license { ], } +genrule { + name: "remote-color-resources-compile-public", + tools: ["aapt2"], + srcs: [ + "remote_color_resources_res/values/public.xml", + ], + out: ["values_public.arsc.flat"], + cmd: "$(location aapt2) compile $(in) -o $(genDir)", +} + +genrule { + name: "remote-color-resources-compile-colors", + tools: ["aapt2"], + srcs: [ + "remote_color_resources_res/values/colors.xml", + ], + out: ["values_colors.arsc.flat"], + cmd: "$(location aapt2) compile $(in) -o $(genDir)", +} + +genrule { + name: "remote-color-resources-apk", + tools: ["aapt2"], + // The first input file in the list must be the manifest + srcs: [ + "RemoteThemeColorsAndroidManifest.xml", + ":remote-color-resources-compile-public", + ":remote-color-resources-compile-colors", + ], + out: ["remote-color-resources.apk"], + cmd: "$(location aapt2) link -o $(out) --manifest $(in)" +} + +genrule { + name: "remote-color-resources-arsc", + srcs: [":remote-color-resources-apk"], + out: ["res/raw/remote_views_color_resources.arsc"], + cmd: "mkdir -p $(genDir)/remote-color-resources-arsc && " + + "unzip -x $(in) resources.arsc -d $(genDir)/remote-color-resources-arsc && " + + "mkdir -p $$(dirname $(out)) && " + + "mv $(genDir)/remote-color-resources-arsc/resources.arsc $(out) && " + + "echo 'Created $(out)'" +} + +genrule { + name: "remote-color-resources-arsc-zip", + tools: ["soong_zip"], + srcs: [ + ":remote-color-resources-arsc", + "remote_color_resources_res/symbols.xml", + ], + out: ["remote_views_color_resources.zip"], + cmd: "INPUTS=($(in)) && " + + "RES_DIR=$$(dirname $$(dirname $${INPUTS[0]})) && " + + "mkdir -p $$RES_DIR/values && " + + "cp $${INPUTS[1]} $$RES_DIR/values && " + + "$(location soong_zip) -o $(out) -C $$RES_DIR -D $$RES_DIR && " + + "cp $(out) ." +} + android_app { name: "framework-res", sdk_version: "core_platform", certificate: "platform", + srcs: [":remote-color-resources-arsc"], + // Disable dexpreopt and verify_uses_libraries check as the app // contains no Java code to be dexpreopted. enforce_uses_libs: false, @@ -72,6 +134,10 @@ android_app { "--auto-add-overlay", ], + resource_zips: [ + ":remote-color-resources-arsc-zip", + ], + // Create package-export.apk, which other packages can use to get // PRODUCT-agnostic resource data like IDs and type definitions. export_package_resources: true, @@ -96,6 +162,7 @@ filegroup { srcs: [ "assets/**/*", "res/**/*", + ":remote-color-resources-arsc", ], } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 5dd85805cfc1..7ad016c8ce47 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2641,6 +2641,16 @@ android:label="@string/permlab_manageProfileAndDeviceOwners" android:description="@string/permdesc_manageProfileAndDeviceOwners" /> + <!-- @TestApi @hide Allows an application to reset the record of previous system update freeze + periods. --> + <permission android:name="android.permission.CLEAR_FREEZE_PERIOD" + android:protectionLevel="signature" /> + + <!-- @TestApi @hide Allows an application to force available DevicePolicyManager logs to + DPC. --> + <permission android:name="android.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS" + android:protectionLevel="signature" /> + <!-- Allows an application to get full detailed information about recently running tasks, with full fidelity to the real state. @hide --> diff --git a/core/res/RemoteThemeColorsAndroidManifest.xml b/core/res/RemoteThemeColorsAndroidManifest.xml new file mode 100644 index 000000000000..11970fd18979 --- /dev/null +++ b/core/res/RemoteThemeColorsAndroidManifest.xml @@ -0,0 +1,5 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android"> +<application/> +</manifest> + diff --git a/core/res/remote_color_resources_res/symbols.xml b/core/res/remote_color_resources_res/symbols.xml new file mode 100644 index 000000000000..82d5e3e2da6d --- /dev/null +++ b/core/res/remote_color_resources_res/symbols.xml @@ -0,0 +1,4 @@ +<resources> + <!-- ARSC file used to overlay local colors when rendering a RemoteViews --> + <java-symbol type="raw" name="remote_views_color_resources" /> +</resources> diff --git a/core/res/remote_color_resources_res/values/colors.xml b/core/res/remote_color_resources_res/values/colors.xml new file mode 100644 index 000000000000..295f16e959e6 --- /dev/null +++ b/core/res/remote_color_resources_res/values/colors.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Note: the values of the colors doesn't really matter (they will always be overwritten before used), but they help a lot debugging, to find out which color is where in the ARSC file. --> + <color name="system_primary_0">#01010101</color> + <color name="system_primary_50">#02020202</color> + <color name="system_primary_100">#03030303</color> + <color name="system_primary_200">#04040404</color> + <color name="system_primary_300">#05050505</color> + <color name="system_primary_400">#06060606</color> + <color name="system_primary_500">#07070707</color> + <color name="system_primary_600">#08080808</color> + <color name="system_primary_700">#09090909</color> + <color name="system_primary_800">#0a0a0a0a</color> + <color name="system_primary_900">#0b0b0b0b</color> + <color name="system_primary_1000">#0c0c0c0c</color> + <color name="system_secondary_0">#10101010</color> + <color name="system_secondary_50">#20202020</color> + <color name="system_secondary_100">#30303030</color> + <color name="system_secondary_200">#40404040</color> + <color name="system_secondary_300">#50505050</color> + <color name="system_secondary_400">#60606060</color> + <color name="system_secondary_500">#70707070</color> + <color name="system_secondary_600">#80808080</color> + <color name="system_secondary_700">#90909090</color> + <color name="system_secondary_800">#a0a0a0a0</color> + <color name="system_secondary_900">#b0b0b0b0</color> + <color name="system_secondary_1000">#c0c0c0c0</color> + <color name="system_neutral_0">#1f1f1f1f</color> + <color name="system_neutral_50">#2f2f2f2f</color> + <color name="system_neutral_100">#3f3f3f3f</color> + <color name="system_neutral_200">#4f4f4f4f</color> + <color name="system_neutral_300">#5f5f5f5f</color> + <color name="system_neutral_400">#6f6f6f6f</color> + <color name="system_neutral_500">#7f7f7f7f</color> + <color name="system_neutral_600">#8f8f8f8f</color> + <color name="system_neutral_700">#9f9f9f9f</color> + <color name="system_neutral_800">#afafafaf</color> + <color name="system_neutral_900">#bfbfbfbf</color> + <color name="system_neutral_1000">#cfcfcfcf</color> +</resources> diff --git a/core/res/remote_color_resources_res/values/public.xml b/core/res/remote_color_resources_res/values/public.xml new file mode 100644 index 000000000000..e628f09b8327 --- /dev/null +++ b/core/res/remote_color_resources_res/values/public.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <public-group type="color" first-id="0x0106001d"> + <public name="system_primary_0" /> + <public name="system_primary_50" /> + <public name="system_primary_100" /> + <public name="system_primary_200" /> + <public name="system_primary_300" /> + <public name="system_primary_400" /> + <public name="system_primary_500" /> + <public name="system_primary_600" /> + <public name="system_primary_700" /> + <public name="system_primary_800" /> + <public name="system_primary_900" /> + <public name="system_primary_1000" /> + <public name="system_secondary_0" /> + <public name="system_secondary_50" /> + <public name="system_secondary_100" /> + <public name="system_secondary_200" /> + <public name="system_secondary_300" /> + <public name="system_secondary_400" /> + <public name="system_secondary_500" /> + <public name="system_secondary_600" /> + <public name="system_secondary_700" /> + <public name="system_secondary_800" /> + <public name="system_secondary_900" /> + <public name="system_secondary_1000" /> + <public name="system_neutral_0" /> + <public name="system_neutral_50" /> + <public name="system_neutral_100" /> + <public name="system_neutral_200" /> + <public name="system_neutral_300" /> + <public name="system_neutral_400" /> + <public name="system_neutral_500" /> + <public name="system_neutral_600" /> + <public name="system_neutral_700" /> + <public name="system_neutral_800" /> + <public name="system_neutral_900" /> + <public name="system_neutral_1000" /> + </public-group> +</resources> diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml index 62114630a328..1de1d049197c 100644 --- a/core/res/res/layout/notification_template_header.xml +++ b/core/res/res/layout/notification_template_header.xml @@ -17,8 +17,8 @@ <NotificationHeaderView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/notification_header" - android:layout_width="wrap_content" - android:layout_height="@dimen/notification_header_solo_height" + android:layout_width="match_parent" + android:layout_height="@dimen/notification_header_height" android:layout_marginBottom="@dimen/notification_header_margin_bottom" android:clipChildren="false" android:gravity="center_vertical" diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml index de537b205866..2d1c3422ca36 100644 --- a/core/res/res/layout/notification_template_material_big_base.xml +++ b/core/res/res/layout/notification_template_material_big_base.xml @@ -38,11 +38,7 @@ android:layout_gravity="top" > - <include - layout="@layout/notification_template_header" - android:layout_width="match_parent" - android:layout_height="@dimen/notification_header_big_height" - /> + <include layout="@layout/notification_template_header" /> <LinearLayout android:id="@+id/notification_main_column" diff --git a/core/res/res/layout/notification_template_material_big_media.xml b/core/res/res/layout/notification_template_material_big_media.xml index 696cb6572e29..bdd4430a7985 100644 --- a/core/res/res/layout/notification_template_material_big_media.xml +++ b/core/res/res/layout/notification_template_material_big_media.xml @@ -36,7 +36,6 @@ layout="@layout/notification_template_header" android:layout_width="match_parent" android:layout_height="@dimen/media_notification_header_height" - android:layout_gravity="start" /> <LinearLayout diff --git a/core/res/res/layout/notification_template_material_big_picture.xml b/core/res/res/layout/notification_template_material_big_picture.xml index e1b7bc4d7bca..6f3c77ff72a4 100644 --- a/core/res/res/layout/notification_template_material_big_picture.xml +++ b/core/res/res/layout/notification_template_material_big_picture.xml @@ -23,11 +23,7 @@ android:clipChildren="false" > - <include - layout="@layout/notification_template_header" - android:layout_width="match_parent" - android:layout_height="@dimen/notification_header_big_height" - /> + <include layout="@layout/notification_template_header" /> <include layout="@layout/notification_template_right_icon" /> diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml index 2452a32b21eb..2954ba2a0903 100644 --- a/core/res/res/layout/notification_template_material_big_text.xml +++ b/core/res/res/layout/notification_template_material_big_text.xml @@ -23,11 +23,7 @@ android:tag="bigText" > - <include - layout="@layout/notification_template_header" - android:layout_width="match_parent" - android:layout_height="@dimen/notification_header_big_height" - /> + <include layout="@layout/notification_template_header" /> <com.android.internal.widget.RemeasuringLinearLayout android:id="@+id/notification_action_list_margin_target" diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml index 7daccd2b8544..baffdd5ac0f1 100644 --- a/core/res/res/layout/notification_template_material_media.xml +++ b/core/res/res/layout/notification_template_material_media.xml @@ -32,7 +32,8 @@ /> <include layout="@layout/notification_template_header" android:layout_width="match_parent" - android:layout_height="@dimen/media_notification_header_height" /> + android:layout_height="@dimen/media_notification_header_height" + /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/core/res/res/values-night/values.xml b/core/res/res/values-night/values.xml index 952cdd08451c..b1bcf7285bb0 100644 --- a/core/res/res/values-night/values.xml +++ b/core/res/res/values-night/values.xml @@ -30,7 +30,7 @@ <style name="Theme.DeviceDefault.QuickSettings.Dialog" parent="Theme.DeviceDefault.Dialog" /> <style name="TextAppearance.Material.Notification"> - <item name="textColor">?attr/textColorPrimary</item> + <item name="textColor">@color/notification_secondary_text_color_dark</item> <item name="textSize">@dimen/notification_text_size</item> </style> </resources>
\ No newline at end of file diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 69bb20cf9c1e..735e122444ef 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3564,6 +3564,16 @@ <attr name="__removed2" format="boolean" /> <!-- Specifies whether the IME supports showing inline suggestions. --> <attr name="supportsInlineSuggestions" format="boolean" /> + <!-- Specify one or more configuration changes that the IME will handle itself. If not + specified, the IME will be restarted if any of these configuration changes happen in + the system. Otherwise, the IME will remain running and its + {@link android.inputmethodservice.InputMethodService#onConfigurationChanged} + method is called with the new configuration. + <p>Note that all of these configuration changes can impact the + resource values seen by the application, so you will generally need + to re-retrieve all resources (including view layouts, drawables, etc) + to correctly handle any configuration change.--> + <attr name="configChanges" /> </declare-styleable> <!-- This is the subtype of InputMethod. Subtype can describe locales (for example, en_US and @@ -4117,6 +4127,94 @@ user's time zone. Please refer to {@link java.util.TimeZone} for more information about time zone ids. --> <attr name="timeZone" format="string"/> + <!-- Tint to apply to the dial graphic. --> + <attr name="dialTint" format="color" /> + <!-- Blending mode used to apply the dial graphic tint. --> + <attr name="dialTintMode"> + <!-- The tint is drawn on top of the drawable. + [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] --> + <enum name="src_over" value="3" /> + <!-- The tint is masked by the alpha channel of the drawable. The drawable’s + color channels are thrown out. [Sa * Da, Sc * Da] --> + <enum name="src_in" value="5" /> + <!-- The tint is drawn above the drawable, but with the drawable’s alpha + channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] --> + <enum name="src_atop" value="9" /> + <!-- Multiplies the color and alpha channels of the drawable with those of + the tint. [Sa * Da, Sc * Dc] --> + <enum name="multiply" value="14" /> + <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] --> + <enum name="screen" value="15" /> + <!-- Combines the tint and drawable color and alpha channels, clamping the + result to valid color values. Saturate(S + D) --> + <enum name="add" value="16" /> + </attr> + <!-- Tint to apply to the hour hand graphic. --> + <attr name="hand_hourTint" format="color" /> + <!-- Blending mode used to apply the hour hand graphic tint. --> + <attr name="hand_hourTintMode"> + <!-- The tint is drawn on top of the drawable. + [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] --> + <enum name="src_over" value="3" /> + <!-- The tint is masked by the alpha channel of the drawable. The drawable’s + color channels are thrown out. [Sa * Da, Sc * Da] --> + <enum name="src_in" value="5" /> + <!-- The tint is drawn above the drawable, but with the drawable’s alpha + channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] --> + <enum name="src_atop" value="9" /> + <!-- Multiplies the color and alpha channels of the drawable with those of + the tint. [Sa * Da, Sc * Dc] --> + <enum name="multiply" value="14" /> + <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] --> + <enum name="screen" value="15" /> + <!-- Combines the tint and drawable color and alpha channels, clamping the + result to valid color values. Saturate(S + D) --> + <enum name="add" value="16" /> + </attr> + <!-- Tint to apply to the minute hand graphic. --> + <attr name="hand_minuteTint" format="color" /> + <!-- Blending mode used to apply the minute hand graphic tint. --> + <attr name="hand_minuteTintMode"> + <!-- The tint is drawn on top of the drawable. + [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] --> + <enum name="src_over" value="3" /> + <!-- The tint is masked by the alpha channel of the drawable. The drawable’s + color channels are thrown out. [Sa * Da, Sc * Da] --> + <enum name="src_in" value="5" /> + <!-- The tint is drawn above the drawable, but with the drawable’s alpha + channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] --> + <enum name="src_atop" value="9" /> + <!-- Multiplies the color and alpha channels of the drawable with those of + the tint. [Sa * Da, Sc * Dc] --> + <enum name="multiply" value="14" /> + <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] --> + <enum name="screen" value="15" /> + <!-- Combines the tint and drawable color and alpha channels, clamping the + result to valid color values. Saturate(S + D) --> + <enum name="add" value="16" /> + </attr> + <!-- Tint to apply to the second hand graphic. --> + <attr name="hand_secondTint" format="color" /> + <!-- Blending mode used to apply the second hand graphic tint. --> + <attr name="hand_secondTintMode"> + <!-- The tint is drawn on top of the drawable. + [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] --> + <enum name="src_over" value="3" /> + <!-- The tint is masked by the alpha channel of the drawable. The drawable’s + color channels are thrown out. [Sa * Da, Sc * Da] --> + <enum name="src_in" value="5" /> + <!-- The tint is drawn above the drawable, but with the drawable’s alpha + channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] --> + <enum name="src_atop" value="9" /> + <!-- Multiplies the color and alpha channels of the drawable with those of + the tint. [Sa * Da, Sc * Dc] --> + <enum name="multiply" value="14" /> + <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] --> + <enum name="screen" value="15" /> + <!-- Combines the tint and drawable color and alpha channels, clamping the + result to valid color values. Saturate(S + D) --> + <enum name="add" value="16" /> + </attr> </declare-styleable> <declare-styleable name="Button"> </declare-styleable> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 0ae6a76e2a60..45e11ba9820e 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1893,6 +1893,11 @@ <!-- User data will remain unchanged during rollback. --> <enum name="retain" value="2" /> </attr> + + <!-- Applications can set this attribute to an xml resource within their app where they + specified the rules determining which files and directories can be copied from the device + as part of backup or transfer operations. --> + <attr name="dataExtractionRules" format="reference"/> </declare-styleable> <!-- An attribution is a logical part of an app and is identified by a tag. diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 9a917b72a5fd..592a3a1ff083 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1173,6 +1173,9 @@ <!-- Default value for LED off time when the battery is low on charge in miliseconds --> <integer name="config_notificationsBatteryLedOff">2875</integer> + <!-- If true, only colorized CallStyle notifications will apply custom colors --> + <bool name="config_callNotificationActionColorsRequireColorized">true</bool> + <!-- Number of notifications to keep in the notification service historical archive --> <integer name="config_notificationServiceArchiveSize">100</integer> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 1ca54985dfbc..695a831faf97 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -271,11 +271,8 @@ <!-- The top margin before the notification progress bar. --> <dimen name="notification_progress_margin_top">8dp</dimen> - <!-- height of the notification header when the notification is alone (minimized / groups) --> - <dimen name="notification_header_solo_height">48dp</dimen> - - <!-- height of the notification header when in a "big" layout --> - <dimen name="notification_header_big_height">56dp</dimen> + <!-- height of the notification header --> + <dimen name="notification_header_height">56dp</dimen> <!-- The height of the background for a notification header on a group --> <dimen name="notification_header_background_height">49.5dp</dimen> @@ -365,7 +362,7 @@ <dimen name="media_notification_expanded_image_margin_bottom">20dp</dimen> <!-- The absolute height for the header in a media notification. --> - <dimen name="media_notification_header_height">@dimen/notification_header_big_height</dimen> + <dimen name="media_notification_header_height">@dimen/notification_header_height</dimen> <!-- The margin of the content to an image--> <dimen name="notification_content_image_margin_end">8dp</dimen> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index a1ea61c447c0..9b2573f3d62f 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3076,6 +3076,15 @@ <public name="maxResizeHeight" /> <public name="targetCellWidth" /> <public name="targetCellHeight" /> + <public name="dialTint"/> + <public name="dialTintMode"/> + <public name="hand_hourTint"/> + <public name="hand_hourTintMode"/> + <public name="hand_minuteTint"/> + <public name="hand_minuteTintMode"/> + <public name="hand_secondTint"/> + <public name="hand_secondTintMode"/> + <public name="dataExtractionRules"/> </public-group> <public-group type="drawable" first-id="0x010800b5"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index e73d6e3af3cb..29b8e6e08947 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2286,6 +2286,7 @@ <java-symbol type="string" name="config_wifi_tether_enable" /> <java-symbol type="bool" name="config_intrusiveNotificationLed" /> <java-symbol type="bool" name="config_notificationBadging" /> + <java-symbol type="bool" name="config_callNotificationActionColorsRequireColorized" /> <java-symbol type="dimen" name="preference_fragment_padding_bottom" /> <java-symbol type="dimen" name="preference_fragment_padding_side" /> <java-symbol type="drawable" name="expander_ic_maximized" /> @@ -4203,6 +4204,4 @@ <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/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index e7e049da18c9..16d720b891e2 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -238,6 +238,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -271,6 +273,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -306,6 +310,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -340,6 +346,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -417,6 +425,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -449,6 +459,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -482,6 +494,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -531,6 +545,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -565,6 +581,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -597,6 +615,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -631,6 +651,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -664,6 +686,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -697,6 +721,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -730,6 +756,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -763,6 +791,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -800,6 +830,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -834,6 +866,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -865,6 +899,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -1069,6 +1105,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1101,6 +1139,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1134,6 +1174,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1169,6 +1211,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1203,6 +1247,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1281,6 +1327,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1316,6 +1364,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1352,6 +1402,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1426,6 +1478,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1463,6 +1517,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1498,6 +1554,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1532,6 +1590,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1565,6 +1625,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1598,6 +1660,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1629,6 +1693,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1750,6 +1816,8 @@ easier. <item name="colorSecondary">@color/secondary_device_default_settings</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -1782,6 +1850,8 @@ easier. <item name="colorSecondary">@color/secondary_device_default_settings</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1824,6 +1894,8 @@ easier. <item name="colorSecondary">@color/secondary_device_default_settings</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1859,6 +1931,8 @@ easier. <item name="colorSecondary">@color/secondary_device_default_settings</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> diff --git a/core/tests/GameManagerTests/Android.bp b/core/tests/GameManagerTests/Android.bp index e1787762e067..2789e9f316d1 100644 --- a/core/tests/GameManagerTests/Android.bp +++ b/core/tests/GameManagerTests/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test { name: "FrameworksCoreGameManagerTests", // Include all test java files diff --git a/core/tests/batterystatstests/BatteryStatsLoadTests/Android.bp b/core/tests/batterystatstests/BatteryStatsLoadTests/Android.bp index ade97b81e775..1c0ea839ec02 100644 --- a/core/tests/batterystatstests/BatteryStatsLoadTests/Android.bp +++ b/core/tests/batterystatstests/BatteryStatsLoadTests/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test { name: "BatteryStatsLoadTests", srcs: ["src/**/*.java"], diff --git a/core/tests/batterystatstests/BatteryStatsViewer/Android.bp b/core/tests/batterystatstests/BatteryStatsViewer/Android.bp index 1e0498be5800..c2e7d81cb283 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/Android.bp +++ b/core/tests/batterystatstests/BatteryStatsViewer/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test { name: "BatteryStatsViewer", srcs: ["src/**/*.java"], diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java b/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java new file mode 100644 index 000000000000..412b36713fa2 --- /dev/null +++ b/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java @@ -0,0 +1,227 @@ +/* + * 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.hardware.input; + +import static android.hardware.lights.LightsRequest.Builder; + +import static com.google.common.truth.Truth.assertThat; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertNotNull; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.lights.Light; +import android.hardware.lights.LightState; +import android.hardware.lights.LightsManager; +import android.os.IBinder; +import android.platform.test.annotations.Presubmit; +import android.util.ArrayMap; +import android.view.InputDevice; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Tests for {@link InputDeviceLightsManager}. + * + * Build/Install/Run: + * atest FrameworksCoreTests:InputDeviceLightsManagerTest + */ +@Presubmit +@RunWith(MockitoJUnitRunner.class) +public class InputDeviceLightsManagerTest { + private static final String TAG = "InputDeviceLightsManagerTest"; + + private static final int DEVICE_ID = 1000; + private static final int PLAYER_ID = 3; + + @Rule public final MockitoRule mockito = MockitoJUnit.rule(); + + private InputManager mInputManager; + + @Mock private IInputManager mIInputManagerMock; + + @Before + public void setUp() throws Exception { + when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID}); + + when(mIInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn( + createInputDevice(DEVICE_ID)); + + mInputManager = InputManager.resetInstance(mIInputManagerMock); + + ArrayMap<Integer, LightState> lightStatesById = new ArrayMap<>(); + doAnswer(invocation -> { + final int[] lightIds = (int[]) invocation.getArguments()[1]; + final LightState[] lightStates = + (LightState[]) invocation.getArguments()[2]; + for (int i = 0; i < lightIds.length; i++) { + lightStatesById.put(lightIds[i], lightStates[i]); + } + return null; + }).when(mIInputManagerMock).setLightStates(eq(DEVICE_ID), + any(int[].class), any(LightState[].class), any(IBinder.class)); + + doAnswer(invocation -> { + int lightId = (int) invocation.getArguments()[1]; + if (lightStatesById.containsKey(lightId)) { + return lightStatesById.get(lightId); + } + return new LightState(0); + }).when(mIInputManagerMock).getLightState(eq(DEVICE_ID), anyInt()); + } + + @After + public void tearDown() { + InputManager.clearInstance(); + } + + private InputDevice createInputDevice(int id) { + return new InputDevice(id, 0 /* generation */, 0 /* controllerNumber */, "name", + 0 /* vendorId */, 0 /* productId */, "descriptor", true /* isExternal */, + 0 /* sources */, 0 /* keyboardType */, null /* keyCharacterMap */, + false /* hasVibrator */, false /* hasMicrophone */, false /* hasButtonUnderpad */, + false /* hasSensor */, false /* hasBattery */); + } + + private void mockLights(Light[] lights) throws Exception { + // Mock the Lights returned form InputManagerService + when(mIInputManagerMock.getLights(eq(DEVICE_ID))).thenReturn( + new ArrayList(Arrays.asList(lights))); + } + + @Test + public void testGetInputDeviceLights() throws Exception { + InputDevice device = mInputManager.getInputDevice(DEVICE_ID); + assertNotNull(device); + + Light[] mockedLights = { + new Light(1 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_SINGLE), + new Light(2 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB), + new Light(3 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_PLAYER_ID) + }; + mockLights(mockedLights); + + LightsManager lightsManager = device.getLightsManager(); + List<Light> lights = lightsManager.getLights(); + verify(mIInputManagerMock).getLights(eq(DEVICE_ID)); + assertEquals(lights, Arrays.asList(mockedLights)); + } + + @Test + public void testControlMultipleLights() throws Exception { + InputDevice device = mInputManager.getInputDevice(DEVICE_ID); + assertNotNull(device); + + Light[] mockedLights = { + new Light(1 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB), + new Light(2 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB), + new Light(3 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB), + new Light(4 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB) + }; + mockLights(mockedLights); + + LightsManager lightsManager = device.getLightsManager(); + List<Light> lightList = lightsManager.getLights(); + LightState[] states = new LightState[]{new LightState(0xf1), new LightState(0xf2), + new LightState(0xf3)}; + // Open a session to request turn 3/4 lights on: + LightsManager.LightsSession session = lightsManager.openSession(); + session.requestLights(new Builder() + .addLight(lightsManager.getLights().get(0), states[0]) + .addLight(lightsManager.getLights().get(1), states[1]) + .addLight(lightsManager.getLights().get(2), states[2]) + .build()); + IBinder token = session.getToken(); + + verify(mIInputManagerMock).openLightSession(eq(DEVICE_ID), + any(String.class), eq(token)); + verify(mIInputManagerMock).setLightStates(eq(DEVICE_ID), eq(new int[]{1, 2, 3}), + eq(states), eq(token)); + + // Then all 3 should turn on. + assertThat(lightsManager.getLightState(lightsManager.getLights().get(0)).getColor()) + .isEqualTo(0xf1); + assertThat(lightsManager.getLightState(lightsManager.getLights().get(1)).getColor()) + .isEqualTo(0xf2); + assertThat(lightsManager.getLightState(lightsManager.getLights().get(2)).getColor()) + .isEqualTo(0xf3); + + // And the 4th should remain off. + assertThat(lightsManager.getLightState(lightsManager.getLights().get(3)).getColor()) + .isEqualTo(0x00); + + // close session + session.close(); + verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token)); + } + + @Test + public void testControlPlayerIdLight() throws Exception { + InputDevice device = mInputManager.getInputDevice(DEVICE_ID); + assertNotNull(device); + + Light[] mockedLights = { + new Light(1 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_PLAYER_ID), + new Light(2 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_SINGLE), + new Light(3 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB), + }; + mockLights(mockedLights); + + LightsManager lightsManager = device.getLightsManager(); + List<Light> lightList = lightsManager.getLights(); + LightState[] states = new LightState[]{new LightState(0xf1, PLAYER_ID)}; + // Open a session to request set Player ID light: + LightsManager.LightsSession session = lightsManager.openSession(); + session.requestLights(new Builder() + .addLight(lightsManager.getLights().get(0), states[0]) + .build()); + IBinder token = session.getToken(); + + verify(mIInputManagerMock).openLightSession(eq(DEVICE_ID), + any(String.class), eq(token)); + verify(mIInputManagerMock).setLightStates(eq(DEVICE_ID), eq(new int[]{1}), + eq(states), eq(token)); + + // Verify the light state + assertThat(lightsManager.getLightState(lightsManager.getLights().get(0)).getColor()) + .isEqualTo(0xf1); + assertThat(lightsManager.getLightState(lightsManager.getLights().get(0)).getPlayerId()) + .isEqualTo(PLAYER_ID); + + // close session + session.close(); + verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token)); + } +} diff --git a/core/tests/coretests/src/android/inputmethodservice/InputMethodServiceTest.java b/core/tests/coretests/src/android/inputmethodservice/InputMethodServiceTest.java new file mode 100644 index 000000000000..890614982ba5 --- /dev/null +++ b/core/tests/coretests/src/android/inputmethodservice/InputMethodServiceTest.java @@ -0,0 +1,80 @@ +/* + * 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.inputmethodservice; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + + +import static junit.framework.Assert.assertFalse; + +import static org.junit.Assert.assertTrue; + +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; +import android.os.Build; + +import androidx.test.filters.SmallTest; +import androidx.test.rule.ServiceTestRule; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.TimeoutException; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class InputMethodServiceTest { + private InputMethodService mService; + private Context mContext; + @Rule + public final ServiceTestRule serviceRule = new ServiceTestRule(); + + @Before + public void setUp() throws TimeoutException { + mContext = getInstrumentation().getContext(); + mService = new InputMethodService(); + } + + @Test + public void testShouldImeRestartForConfig() throws Exception { + // Make sure we preserve Pre-S behavior i.e. Service restarts. + mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.R; + Configuration config = mContext.getResources().getConfiguration(); + mService.setLastKnownConfig(config); + assertTrue("IME should restart for Pre-S", + mService.shouldImeRestartForConfig(config)); + + // IME shouldn't restart on targetSdk S+ (with no config changes). + mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.S; + assertFalse("IME shouldn't restart for S+", + mService.shouldImeRestartForConfig(config)); + + // Screen density changed but IME doesn't handle congfigChanges + config.densityDpi = 99; + assertTrue("IME should restart for unhandled configChanges", + mService.shouldImeRestartForConfig(config)); + + // opt-in IME to handle config changes. + mService.setHandledConfigChanges(ActivityInfo.CONFIG_DENSITY); + assertFalse("IME shouldn't restart for S+ since it handles configChanges", + mService.shouldImeRestartForConfig(config)); + } +} diff --git a/core/tests/coretests/src/android/util/RotationUtilsTest.java b/core/tests/coretests/src/android/util/RotationUtilsTest.java new file mode 100644 index 000000000000..5dbe03e8b42b --- /dev/null +++ b/core/tests/coretests/src/android/util/RotationUtilsTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +import static android.util.RotationUtils.rotateBounds; +import static android.view.Surface.ROTATION_180; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; + +import static org.junit.Assert.assertEquals; + +import android.graphics.Rect; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for {@link RotationUtils}. + * + * Build/Install/Run: + * atest FrameworksCoreTests:RotationUtilsTest + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class RotationUtilsTest { + + @Test + public void testRotateBounds() { + Rect testParent = new Rect(0, 0, 1000, 600); + Rect testInner = new Rect(40, 20, 120, 80); + + Rect testResult = new Rect(testInner); + rotateBounds(testResult, testParent, ROTATION_90); + assertEquals(new Rect(20, 880, 80, 960), testResult); + + testResult.set(testInner); + rotateBounds(testResult, testParent, ROTATION_180); + assertEquals(new Rect(880, 520, 960, 580), testResult); + + testResult.set(testInner); + rotateBounds(testResult, testParent, ROTATION_270); + assertEquals(new Rect(520, 40, 580, 120), testResult); + } +} diff --git a/core/tests/coretests/src/android/view/OWNERS b/core/tests/coretests/src/android/view/OWNERS index 5031ff913e6d..80165f065995 100644 --- a/core/tests/coretests/src/android/view/OWNERS +++ b/core/tests/coretests/src/android/view/OWNERS @@ -1,7 +1,7 @@ # Input -per-file *MotionEventTest.* = michaelwr@google.com, svv@google.com -per-file *KeyEventTest.* = michaelwr@google.com, svv@google.com -per-file VelocityTest.java = michaelwr@google.com, svv@google.com +per-file *MotionEventTest.* = file:/services/core/java/com/android/server/input/OWNERS +per-file *KeyEventTest.* = file:/services/core/java/com/android/server/input/OWNERS +per-file VelocityTest.java = file:/services/core/java/com/android/server/input/OWNERS # WindowManager per-file *Display* = file:/services/core/java/com/android/server/wm/OWNERS diff --git a/core/tests/coretests/src/android/view/SurfaceControlFpsListenerTest.java b/core/tests/coretests/src/android/view/SurfaceControlFpsListenerTest.java new file mode 100644 index 000000000000..36104cf0f71d --- /dev/null +++ b/core/tests/coretests/src/android/view/SurfaceControlFpsListenerTest.java @@ -0,0 +1,46 @@ +/* + * 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 android.view; + +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 SurfaceControlFpsListenerTest { + + @Test + public void registersAndUnregisters() { + + SurfaceControlFpsListener listener = new SurfaceControlFpsListener() { + @Override + public void onFpsReported(float fps) { + // Ignore + } + }; + + listener.register(new SurfaceControl()); + + listener.unregister(); + } +} diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java index 10aaf317fb49..9cb7876b3e5a 100644 --- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java +++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java @@ -105,7 +105,8 @@ public class FrameTrackerTest { mTracker = Mockito.spy( new FrameTracker(session, handler, mRenderer, mViewRootWrapper, mSurfaceControlWrapper, mChoreographer, mWrapper, - /*traceThresholdMissedFrames=*/ 1, /*traceThresholdFrameTimeMillis=*/ -1)); + /*traceThresholdMissedFrames=*/ 1, /*traceThresholdFrameTimeMillis=*/ -1, + null)); doNothing().when(mTracker).triggerPerfetto(); } diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java index c4c475b6a0e9..8f4948c02a74 100644 --- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java +++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java @@ -96,7 +96,7 @@ public class InteractionJankMonitorTest { new ViewRootWrapper(mView.getViewRootImpl()), new SurfaceControlWrapper(), mock(FrameTracker.ChoreographerWrapper.class), new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1, - /*traceThresholdFrameTimeMillis=*/ -1)); + /*traceThresholdFrameTimeMillis=*/ -1, null)); doReturn(tracker).when(monitor).createFrameTracker(any(), any()); // Simulate a trace session and see if begin / end are invoked. @@ -123,21 +123,12 @@ public class InteractionJankMonitorTest { @Test public void testCheckInitState() { InteractionJankMonitor monitor = new InteractionJankMonitor(mWorker); + View view = new View(mActivity); + assertThat(view.isAttachedToWindow()).isFalse(); - // Should return false if invoking begin / end without init invocation. - assertThat(monitor.begin(mView, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse(); + // Should return false if the view passed in is not attached to window yet. + assertThat(monitor.begin(view, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse(); assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse(); - - // Everything should be fine if invoking init first. - boolean thrown = false; - try { - assertThat(monitor.begin(mView, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue(); - assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue(); - } catch (Exception ex) { - thrown = true; - } finally { - assertThat(thrown).isFalse(); - } } @Test @@ -152,7 +143,7 @@ public class InteractionJankMonitorTest { new ViewRootWrapper(mView.getViewRootImpl()), new SurfaceControlWrapper(), mock(FrameTracker.ChoreographerWrapper.class), new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1, - /*traceThresholdFrameTimeMillis=*/ -1)); + /*traceThresholdFrameTimeMillis=*/ -1, null)); doReturn(tracker).when(monitor).createFrameTracker(any(), any()); assertThat(monitor.begin(mView, session.getCuj())).isTrue(); diff --git a/core/tests/devicestatetests/Android.bp b/core/tests/devicestatetests/Android.bp index 409b77bc399e..f7b593264cda 100644 --- a/core/tests/devicestatetests/Android.bp +++ b/core/tests/devicestatetests/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test { name: "FrameworksCoreDeviceStateManagerTests", // Include all test java files diff --git a/core/tests/overlaytests/remount/test-apps/overlaid_apex/Android.bp b/core/tests/overlaytests/remount/test-apps/overlaid_apex/Android.bp index 42421ce0e9f3..3536c4088dd8 100644 --- a/core/tests/overlaytests/remount/test-apps/overlaid_apex/Android.bp +++ b/core/tests/overlaytests/remount/test-apps/overlaid_apex/Android.bp @@ -50,4 +50,5 @@ apex { key: "com.android.overlaytest.overlaid.key", apps: ["OverlayRemountedTest_Target"], installable: false, + updatable: false, } diff --git a/core/tests/overlaytests/remount/test-apps/overlay_apex/Android.bp b/core/tests/overlaytests/remount/test-apps/overlay_apex/Android.bp index 0b52dcc4fb85..f04140409bea 100644 --- a/core/tests/overlaytests/remount/test-apps/overlay_apex/Android.bp +++ b/core/tests/overlaytests/remount/test-apps/overlay_apex/Android.bp @@ -50,4 +50,5 @@ apex { key: "com.android.overlaytest.overlay.key", apps: ["OverlayRemountedTest_Overlay"], installable: false, + updatable: false, } diff --git a/data/etc/car/com.google.android.car.networking.preferenceupdater.xml b/data/etc/car/com.google.android.car.networking.preferenceupdater.xml index 489ce1b47ffa..cdeb8e48a59b 100644 --- a/data/etc/car/com.google.android.car.networking.preferenceupdater.xml +++ b/data/etc/car/com.google.android.car.networking.preferenceupdater.xml @@ -16,12 +16,21 @@ --> <permissions> <privapp-permissions package="com.google.android.car.networking.preferenceupdater"> - <permission name="android.permission.ACCESS_NETWORK_STATE"/> + <permission name="android.permission.ACCESS_NETWORK_STATE" /> <permission name="android.permission.ACCESS_WIFI_STATE"/> <permission name="android.permission.ACTIVITY_EMBEDDING"/> + <permission name="android.permission.CHANGE_NETWORK_STATE" /> + <permission name="android.permission.CONNECTIVITY_INTERNAL" /> + <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/> + <permission name="android.permission.CONTROL_OEM_PAID_NETWORK_PREFERNCE" /> <permission name="android.permission.INTERACT_ACROSS_USERS"/> <permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/> - <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/> + <permission name="android.permission.INTERNET" /> <permission name="android.permission.LOCATION_HARDWARE"/> + <permission name="android.permission.PACKAGE_USAGE_STATS" /> + <permission name="android.permission.RECEIVE_BOOT_COMPLETED" /> + <permission name="android.permission.WAKE_LOCK" /> + <permission name="android.permission.WRITE_SETTINGS" /> + <permission name="android.car.permission.CAR_DRIVING_STATE" /> </privapp-permissions> </permissions> diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml index 2db4c5d6bf2a..4f188cc03282 100644 --- a/data/fonts/fonts.xml +++ b/data/fonts/fonts.xml @@ -223,7 +223,7 @@ <alias name="sans-serif-condensed-medium" to="sans-serif-condensed" weight="500" /> <family name="serif"> - <font weight="400" style="normal">NotoSerif-Regular.ttf</font> + <font weight="400" style="normal">NotoSerif.ttf</font> <font weight="700" style="normal">NotoSerif-Bold.ttf</font> <font weight="400" style="italic">NotoSerif-Italic.ttf</font> <font weight="700" style="italic">NotoSerif-BoldItalic.ttf</font> @@ -275,144 +275,144 @@ <!-- fallback fonts --> <family lang="und-Arab" variant="elegant"> - <font weight="400" style="normal">NotoNaskhArabic-Regular.ttf</font> + <font weight="400" style="normal">NotoNaskhArabic.ttf</font> <font weight="700" style="normal">NotoNaskhArabic-Bold.ttf</font> </family> <family lang="und-Arab" variant="compact"> - <font weight="400" style="normal">NotoNaskhArabicUI-Regular.ttf</font> + <font weight="400" style="normal">NotoNaskhArabicUI.ttf</font> <font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font> </family> <family lang="und-Ethi"> - <font weight="400" style="normal">NotoSansEthiopic-VF.ttf + <font weight="400" style="normal">NotoSansEthiopic-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansEthiopic-VF.ttf + <font weight="500" style="normal">NotoSansEthiopic-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansEthiopic-VF.ttf + <font weight="600" style="normal">NotoSansEthiopic-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansEthiopic-VF.ttf + <font weight="700" style="normal">NotoSansEthiopic-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifEthiopic-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifEthiopic-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifEthiopic-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifEthiopic-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Hebr"> - <font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font> + <font weight="400" style="normal">NotoSansHebrew.ttf</font> <font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font> <font weight="400" style="normal" fallbackFor="serif">NotoSerifHebrew-Regular.ttf</font> <font weight="700" style="normal" fallbackFor="serif">NotoSerifHebrew-Bold.ttf</font> </family> <family lang="und-Thai" variant="elegant"> - <font weight="400" style="normal">NotoSansThai-Regular.ttf</font> + <font weight="400" style="normal">NotoSansThai.ttf</font> <font weight="700" style="normal">NotoSansThai-Bold.ttf</font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifThai-Regular.ttf</font> + <font weight="400" style="normal" fallbackFor="serif">NotoSerifThai.ttf</font> <font weight="700" style="normal" fallbackFor="serif">NotoSerifThai-Bold.ttf</font> </family> <family lang="und-Thai" variant="compact"> - <font weight="400" style="normal">NotoSansThaiUI-Regular.ttf</font> + <font weight="400" style="normal">NotoSansThaiUI.ttf</font> <font weight="700" style="normal">NotoSansThaiUI-Bold.ttf</font> </family> <family lang="und-Armn"> - <font weight="400" style="normal">NotoSansArmenian-VF.ttf + <font weight="400" style="normal">NotoSansArmenian-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansArmenian-VF.ttf + <font weight="500" style="normal">NotoSansArmenian-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansArmenian-VF.ttf + <font weight="600" style="normal">NotoSansArmenian-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansArmenian-VF.ttf + <font weight="700" style="normal">NotoSansArmenian-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifArmenian-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifArmenian-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifArmenian-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifArmenian-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Geor,und-Geok"> - <font weight="400" style="normal">NotoSansGeorgian-VF.ttf + <font weight="400" style="normal">NotoSansGeorgian-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansGeorgian-VF.ttf + <font weight="500" style="normal">NotoSansGeorgian-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansGeorgian-VF.ttf + <font weight="600" style="normal">NotoSansGeorgian-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansGeorgian-VF.ttf + <font weight="700" style="normal">NotoSansGeorgian-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifGeorgian-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifGeorgian-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifGeorgian-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifGeorgian-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifGeorgian-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifGeorgian-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifGeorgian-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifGeorgian-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Deva" variant="elegant"> - <font weight="400" style="normal">NotoSansDevanagari-VF.ttf + <font weight="400" style="normal">NotoSansDevanagari-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansDevanagari-VF.ttf + <font weight="500" style="normal">NotoSansDevanagari-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansDevanagari-VF.ttf + <font weight="600" style="normal">NotoSansDevanagari-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansDevanagari-VF.ttf + <font weight="700" style="normal">NotoSansDevanagari-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifDevanagari-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifDevanagari-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifDevanagari-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifDevanagari-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifDevanagari-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifDevanagari-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifDevanagari-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifDevanagari-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Deva" variant="compact"> - <font weight="400" style="normal">NotoSansDevanagariUI-VF.ttf + <font weight="400" style="normal">NotoSansDevanagariUI-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansDevanagariUI-VF.ttf + <font weight="500" style="normal">NotoSansDevanagariUI-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansDevanagariUI-VF.ttf + <font weight="600" style="normal">NotoSansDevanagariUI-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansDevanagariUI-VF.ttf + <font weight="700" style="normal">NotoSansDevanagariUI-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> @@ -421,347 +421,347 @@ danda characters. --> <family lang="und-Gujr" variant="elegant"> - <font weight="400" style="normal">NotoSansGujarati-Regular.ttf</font> + <font weight="400" style="normal">NotoSansGujarati.ttf</font> <font weight="700" style="normal">NotoSansGujarati-Bold.ttf</font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifGujarati-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifGujarati-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifGujarati-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifGujarati-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifGujarati-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifGujarati-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifGujarati-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifGujarati-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Gujr" variant="compact"> - <font weight="400" style="normal">NotoSansGujaratiUI-Regular.ttf</font> + <font weight="400" style="normal">NotoSansGujaratiUI.ttf</font> <font weight="700" style="normal">NotoSansGujaratiUI-Bold.ttf</font> </family> <family lang="und-Guru" variant="elegant"> - <font weight="400" style="normal">NotoSansGurmukhi-VF.ttf + <font weight="400" style="normal">NotoSansGurmukhi-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansGurmukhi-VF.ttf + <font weight="500" style="normal">NotoSansGurmukhi-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansGurmukhi-VF.ttf + <font weight="600" style="normal">NotoSansGurmukhi-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansGurmukhi-VF.ttf + <font weight="700" style="normal">NotoSansGurmukhi-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifGurmukhi-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifGurmukhi-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifGurmukhi-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifGurmukhi-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifGurmukhi-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifGurmukhi-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifGurmukhi-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifGurmukhi-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Guru" variant="compact"> - <font weight="400" style="normal">NotoSansGurmukhiUI-VF.ttf + <font weight="400" style="normal">NotoSansGurmukhiUI-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansGurmukhiUI-VF.ttf + <font weight="500" style="normal">NotoSansGurmukhiUI-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansGurmukhiUI-VF.ttf + <font weight="600" style="normal">NotoSansGurmukhiUI-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansGurmukhiUI-VF.ttf + <font weight="700" style="normal">NotoSansGurmukhiUI-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Taml" variant="elegant"> - <font weight="400" style="normal">NotoSansTamil-VF.ttf + <font weight="400" style="normal">NotoSansTamil-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansTamil-VF.ttf + <font weight="500" style="normal">NotoSansTamil-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansTamil-VF.ttf + <font weight="600" style="normal">NotoSansTamil-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansTamil-VF.ttf + <font weight="700" style="normal">NotoSansTamil-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifTamil-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifTamil-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifTamil-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifTamil-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifTamil-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifTamil-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifTamil-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifTamil-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Taml" variant="compact"> - <font weight="400" style="normal">NotoSansTamilUI-VF.ttf + <font weight="400" style="normal">NotoSansTamilUI-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansTamilUI-VF.ttf + <font weight="500" style="normal">NotoSansTamilUI-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansTamilUI-VF.ttf + <font weight="600" style="normal">NotoSansTamilUI-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansTamilUI-VF.ttf + <font weight="700" style="normal">NotoSansTamilUI-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Mlym" variant="elegant"> - <font weight="400" style="normal">NotoSansMalayalam-VF.ttf + <font weight="400" style="normal">NotoSansMalayalam-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansMalayalam-VF.ttf + <font weight="500" style="normal">NotoSansMalayalam-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansMalayalam-VF.ttf + <font weight="600" style="normal">NotoSansMalayalam-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansMalayalam-VF.ttf + <font weight="700" style="normal">NotoSansMalayalam-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifMalayalam-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifMalayalam-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifMalayalam-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifMalayalam-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifMalayalam-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifMalayalam-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifMalayalam-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifMalayalam-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Mlym" variant="compact"> - <font weight="400" style="normal">NotoSansMalayalamUI-VF.ttf + <font weight="400" style="normal">NotoSansMalayalamUI-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansMalayalamUI-VF.ttf + <font weight="500" style="normal">NotoSansMalayalamUI-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansMalayalamUI-VF.ttf + <font weight="600" style="normal">NotoSansMalayalamUI-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansMalayalamUI-VF.ttf + <font weight="700" style="normal">NotoSansMalayalamUI-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Beng" variant="elegant"> - <font weight="400" style="normal">NotoSansBengali-VF.ttf + <font weight="400" style="normal">NotoSansBengali-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansBengali-VF.ttf + <font weight="500" style="normal">NotoSansBengali-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansBengali-VF.ttf + <font weight="600" style="normal">NotoSansBengali-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansBengali-VF.ttf + <font weight="700" style="normal">NotoSansBengali-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifBengali-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifBengali-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifBengali-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifBengali-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifBengali-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifBengali-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifBengali-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifBengali-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Beng" variant="compact"> - <font weight="400" style="normal">NotoSansBengaliUI-VF.ttf + <font weight="400" style="normal">NotoSansBengaliUI-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansBengaliUI-VF.ttf + <font weight="500" style="normal">NotoSansBengaliUI-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansBengaliUI-VF.ttf + <font weight="600" style="normal">NotoSansBengaliUI-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansBengaliUI-VF.ttf + <font weight="700" style="normal">NotoSansBengaliUI-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Telu" variant="elegant"> - <font weight="400" style="normal">NotoSansTelugu-VF.ttf + <font weight="400" style="normal">NotoSansTelugu-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansTelugu-VF.ttf + <font weight="500" style="normal">NotoSansTelugu-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansTelugu-VF.ttf + <font weight="600" style="normal">NotoSansTelugu-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansTelugu-VF.ttf + <font weight="700" style="normal">NotoSansTelugu-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifTelugu-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifTelugu-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifTelugu-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifTelugu-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifTelugu-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifTelugu-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifTelugu-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifTelugu-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Telu" variant="compact"> - <font weight="400" style="normal">NotoSansTeluguUI-VF.ttf + <font weight="400" style="normal">NotoSansTeluguUI-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansTeluguUI-VF.ttf + <font weight="500" style="normal">NotoSansTeluguUI-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansTeluguUI-VF.ttf + <font weight="600" style="normal">NotoSansTeluguUI-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansTeluguUI-VF.ttf + <font weight="700" style="normal">NotoSansTeluguUI-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Knda" variant="elegant"> - <font weight="400" style="normal">NotoSansKannada-VF.ttf + <font weight="400" style="normal">NotoSansKannada-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansKannada-VF.ttf + <font weight="500" style="normal">NotoSansKannada-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansKannada-VF.ttf + <font weight="600" style="normal">NotoSansKannada-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansKannada-VF.ttf + <font weight="700" style="normal">NotoSansKannada-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifKannada-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifKannada-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifKannada-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifKannada-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifKannada-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifKannada-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifKannada-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifKannada-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Knda" variant="compact"> - <font weight="400" style="normal">NotoSansKannadaUI-VF.ttf + <font weight="400" style="normal">NotoSansKannadaUI-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansKannadaUI-VF.ttf + <font weight="500" style="normal">NotoSansKannadaUI-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansKannadaUI-VF.ttf + <font weight="600" style="normal">NotoSansKannadaUI-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansKannadaUI-VF.ttf + <font weight="700" style="normal">NotoSansKannadaUI-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Orya" variant="elegant"> - <font weight="400" style="normal">NotoSansOriya-Regular.ttf</font> + <font weight="400" style="normal">NotoSansOriya.ttf</font> <font weight="700" style="normal">NotoSansOriya-Bold.ttf</font> </family> <family lang="und-Orya" variant="compact"> - <font weight="400" style="normal">NotoSansOriyaUI-Regular.ttf</font> + <font weight="400" style="normal">NotoSansOriyaUI.ttf</font> <font weight="700" style="normal">NotoSansOriyaUI-Bold.ttf</font> </family> <family lang="und-Sinh" variant="elegant"> - <font weight="400" style="normal">NotoSansSinhala-VF.ttf + <font weight="400" style="normal">NotoSansSinhala-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansSinhala-VF.ttf + <font weight="500" style="normal">NotoSansSinhala-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansSinhala-VF.ttf + <font weight="600" style="normal">NotoSansSinhala-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansSinhala-VF.ttf + <font weight="700" style="normal">NotoSansSinhala-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifSinhala-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifSinhala-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifSinhala-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifSinhala-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifSinhala-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifSinhala-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifSinhala-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifSinhala-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Sinh" variant="compact"> - <font weight="400" style="normal">NotoSansSinhalaUI-VF.ttf + <font weight="400" style="normal">NotoSansSinhalaUI-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansSinhalaUI-VF.ttf + <font weight="500" style="normal">NotoSansSinhalaUI-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansSinhalaUI-VF.ttf + <font weight="600" style="normal">NotoSansSinhalaUI-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansSinhalaUI-VF.ttf + <font weight="700" style="normal">NotoSansSinhalaUI-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Khmr" variant="elegant"> - <font weight="100" style="normal">NotoSansKhmer-VF.ttf + <font weight="100" style="normal">NotoSansKhmer-Regular.ttf <axis tag="wdth" stylevalue="100.0"/> <axis tag="wght" stylevalue="26.0"/> </font> - <font weight="200" style="normal">NotoSansKhmer-VF.ttf + <font weight="200" style="normal">NotoSansKhmer-Regular.ttf <axis tag="wdth" stylevalue="100.0"/> <axis tag="wght" stylevalue="39.0"/> </font> - <font weight="300" style="normal">NotoSansKhmer-VF.ttf + <font weight="300" style="normal">NotoSansKhmer-Regular.ttf <axis tag="wdth" stylevalue="100.0"/> <axis tag="wght" stylevalue="58.0"/> </font> - <font weight="400" style="normal">NotoSansKhmer-VF.ttf + <font weight="400" style="normal">NotoSansKhmer-Regular.ttf <axis tag="wdth" stylevalue="100.0"/> <axis tag="wght" stylevalue="90.0"/> </font> - <font weight="500" style="normal">NotoSansKhmer-VF.ttf + <font weight="500" style="normal">NotoSansKhmer-Regular.ttf <axis tag="wdth" stylevalue="100.0"/> <axis tag="wght" stylevalue="108.0"/> </font> - <font weight="600" style="normal">NotoSansKhmer-VF.ttf + <font weight="600" style="normal">NotoSansKhmer-Regular.ttf <axis tag="wdth" stylevalue="100.0"/> <axis tag="wght" stylevalue="128.0"/> </font> - <font weight="700" style="normal">NotoSansKhmer-VF.ttf + <font weight="700" style="normal">NotoSansKhmer-Regular.ttf <axis tag="wdth" stylevalue="100.0"/> <axis tag="wght" stylevalue="151.0"/> </font> - <font weight="800" style="normal">NotoSansKhmer-VF.ttf + <font weight="800" style="normal">NotoSansKhmer-Regular.ttf <axis tag="wdth" stylevalue="100.0"/> <axis tag="wght" stylevalue="169.0"/> </font> - <font weight="900" style="normal">NotoSansKhmer-VF.ttf + <font weight="900" style="normal">NotoSansKhmer-Regular.ttf <axis tag="wdth" stylevalue="100.0"/> <axis tag="wght" stylevalue="190.0"/> </font> @@ -769,17 +769,17 @@ <font weight="700" style="normal" fallbackFor="serif">NotoSerifKhmer-Bold.otf</font> </family> <family lang="und-Khmr" variant="compact"> - <font weight="400" style="normal">NotoSansKhmerUI-Regular.ttf</font> + <font weight="400" style="normal">NotoSansKhmerUI.ttf</font> <font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font> </family> <family lang="und-Laoo" variant="elegant"> - <font weight="400" style="normal">NotoSansLao-Regular.ttf</font> + <font weight="400" style="normal">NotoSansLao.ttf</font> <font weight="700" style="normal">NotoSansLao-Bold.ttf</font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifLao-Regular.ttf</font> + <font weight="400" style="normal" fallbackFor="serif">NotoSerifLao.ttf</font> <font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font> </family> <family lang="und-Laoo" variant="compact"> - <font weight="400" style="normal">NotoSansLaoUI-Regular.ttf</font> + <font weight="400" style="normal">NotoSansLaoUI.ttf</font> <font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font> </family> <family lang="und-Mymr" variant="elegant"> @@ -795,56 +795,56 @@ <font weight="700" style="normal">NotoSansMyanmarUI-Bold.otf</font> </family> <family lang="und-Thaa"> - <font weight="400" style="normal">NotoSansThaana-Regular.ttf</font> + <font weight="400" style="normal">NotoSansThaana.ttf</font> <font weight="700" style="normal">NotoSansThaana-Bold.ttf</font> </family> <family lang="und-Cham"> - <font weight="400" style="normal">NotoSansCham-Regular.ttf</font> + <font weight="400" style="normal">NotoSansCham.ttf</font> <font weight="700" style="normal">NotoSansCham-Bold.ttf</font> </family> <family lang="und-Ahom"> <font weight="400" style="normal">NotoSansAhom-Regular.otf</font> </family> <family lang="und-Adlm"> - <font weight="400" style="normal">NotoSansAdlam-VF.ttf + <font weight="400" style="normal">NotoSansAdlam-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansAdlam-VF.ttf + <font weight="500" style="normal">NotoSansAdlam-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansAdlam-VF.ttf + <font weight="600" style="normal">NotoSansAdlam-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansAdlam-VF.ttf + <font weight="700" style="normal">NotoSansAdlam-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Avst"> - <font weight="400" style="normal">NotoSansAvestan-Regular.ttf</font> + <font weight="400" style="normal">NotoSansAvestan.ttf</font> </family> <family lang="und-Bali"> - <font weight="400" style="normal">NotoSansBalinese-Regular.ttf</font> + <font weight="400" style="normal">NotoSansBalinese.ttf</font> </family> <family lang="und-Bamu"> - <font weight="400" style="normal">NotoSansBamum-Regular.ttf</font> + <font weight="400" style="normal">NotoSansBamum.ttf</font> </family> <family lang="und-Batk"> - <font weight="400" style="normal">NotoSansBatak-Regular.ttf</font> + <font weight="400" style="normal">NotoSansBatak.ttf</font> </family> <family lang="und-Brah"> - <font weight="400" style="normal">NotoSansBrahmi-Regular.ttf</font> + <font weight="400" style="normal">NotoSansBrahmi.ttf</font> </family> <family lang="und-Bugi"> - <font weight="400" style="normal">NotoSansBuginese-Regular.ttf</font> + <font weight="400" style="normal">NotoSansBuginese.ttf</font> </family> <family lang="und-Buhd"> - <font weight="400" style="normal">NotoSansBuhid-Regular.ttf</font> + <font weight="400" style="normal">NotoSansBuhid.ttf</font> </family> <family lang="und-Cans"> - <font weight="400" style="normal">NotoSansCanadianAboriginal-Regular.ttf</font> + <font weight="400" style="normal">NotoSansCanadianAboriginal.ttf</font> </family> <family lang="und-Cari"> - <font weight="400" style="normal">NotoSansCarian-Regular.ttf</font> + <font weight="400" style="normal">NotoSansCarian.ttf</font> </family> <family lang="und-Cakm"> <font weight="400" style="normal">NotoSansChakma-Regular.otf</font> @@ -853,164 +853,164 @@ <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font> </family> <family lang="und-Copt"> - <font weight="400" style="normal">NotoSansCoptic-Regular.ttf</font> + <font weight="400" style="normal">NotoSansCoptic.ttf</font> </family> <family lang="und-Xsux"> - <font weight="400" style="normal">NotoSansCuneiform-Regular.ttf</font> + <font weight="400" style="normal">NotoSansCuneiform.ttf</font> </family> <family lang="und-Cprt"> - <font weight="400" style="normal">NotoSansCypriot-Regular.ttf</font> + <font weight="400" style="normal">NotoSansCypriot.ttf</font> </family> <family lang="und-Dsrt"> - <font weight="400" style="normal">NotoSansDeseret-Regular.ttf</font> + <font weight="400" style="normal">NotoSansDeseret.ttf</font> </family> <family lang="und-Egyp"> - <font weight="400" style="normal">NotoSansEgyptianHieroglyphs-Regular.ttf</font> + <font weight="400" style="normal">NotoSansEgyptianHieroglyphs.ttf</font> </family> <family lang="und-Elba"> <font weight="400" style="normal">NotoSansElbasan-Regular.otf</font> </family> <family lang="und-Glag"> - <font weight="400" style="normal">NotoSansGlagolitic-Regular.ttf</font> + <font weight="400" style="normal">NotoSansGlagolitic.ttf</font> </family> <family lang="und-Goth"> - <font weight="400" style="normal">NotoSansGothic-Regular.ttf</font> + <font weight="400" style="normal">NotoSansGothic.ttf</font> </family> <family lang="und-Hano"> - <font weight="400" style="normal">NotoSansHanunoo-Regular.ttf</font> + <font weight="400" style="normal">NotoSansHanunoo.ttf</font> </family> <family lang="und-Armi"> - <font weight="400" style="normal">NotoSansImperialAramaic-Regular.ttf</font> + <font weight="400" style="normal">NotoSansImperialAramaic.ttf</font> </family> <family lang="und-Phli"> - <font weight="400" style="normal">NotoSansInscriptionalPahlavi-Regular.ttf</font> + <font weight="400" style="normal">NotoSansInscriptionalPahlavi.ttf</font> </family> <family lang="und-Prti"> - <font weight="400" style="normal">NotoSansInscriptionalParthian-Regular.ttf</font> + <font weight="400" style="normal">NotoSansInscriptionalParthian.ttf</font> </family> <family lang="und-Java"> <font weight="400" style="normal">NotoSansJavanese-Regular.otf</font> </family> <family lang="und-Kthi"> - <font weight="400" style="normal">NotoSansKaithi-Regular.ttf</font> + <font weight="400" style="normal">NotoSansKaithi.ttf</font> </family> <family lang="und-Kali"> - <font weight="400" style="normal">NotoSansKayahLi-Regular.ttf</font> + <font weight="400" style="normal">NotoSansKayahLi.ttf</font> </family> <family lang="und-Khar"> - <font weight="400" style="normal">NotoSansKharoshthi-Regular.ttf</font> + <font weight="400" style="normal">NotoSansKharoshthi.ttf</font> </family> <family lang="und-Lepc"> - <font weight="400" style="normal">NotoSansLepcha-Regular.ttf</font> + <font weight="400" style="normal">NotoSansLepcha.ttf</font> </family> <family lang="und-Limb"> - <font weight="400" style="normal">NotoSansLimbu-Regular.ttf</font> + <font weight="400" style="normal">NotoSansLimbu.ttf</font> </family> <family lang="und-Linb"> - <font weight="400" style="normal">NotoSansLinearB-Regular.ttf</font> + <font weight="400" style="normal">NotoSansLinearB.ttf</font> </family> <family lang="und-Lisu"> - <font weight="400" style="normal">NotoSansLisu-Regular.ttf</font> + <font weight="400" style="normal">NotoSansLisu.ttf</font> </family> <family lang="und-Lyci"> - <font weight="400" style="normal">NotoSansLycian-Regular.ttf</font> + <font weight="400" style="normal">NotoSansLycian.ttf</font> </family> <family lang="und-Lydi"> - <font weight="400" style="normal">NotoSansLydian-Regular.ttf</font> + <font weight="400" style="normal">NotoSansLydian.ttf</font> </family> <family lang="und-Mand"> - <font weight="400" style="normal">NotoSansMandaic-Regular.ttf</font> + <font weight="400" style="normal">NotoSansMandaic.ttf</font> </family> <family lang="und-Mtei"> - <font weight="400" style="normal">NotoSansMeeteiMayek-Regular.ttf</font> + <font weight="400" style="normal">NotoSansMeeteiMayek.ttf</font> </family> <family lang="und-Talu"> - <font weight="400" style="normal">NotoSansNewTaiLue-Regular.ttf</font> + <font weight="400" style="normal">NotoSansNewTaiLue.ttf</font> </family> <family lang="und-Nkoo"> - <font weight="400" style="normal">NotoSansNKo-Regular.ttf</font> + <font weight="400" style="normal">NotoSansNKo.ttf</font> </family> <family lang="und-Ogam"> - <font weight="400" style="normal">NotoSansOgham-Regular.ttf</font> + <font weight="400" style="normal">NotoSansOgham.ttf</font> </family> <family lang="und-Olck"> - <font weight="400" style="normal">NotoSansOlChiki-Regular.ttf</font> + <font weight="400" style="normal">NotoSansOlChiki.ttf</font> </family> <family lang="und-Ital"> - <font weight="400" style="normal">NotoSansOldItalic-Regular.ttf</font> + <font weight="400" style="normal">NotoSansOldItalic.ttf</font> </family> <family lang="und-Xpeo"> - <font weight="400" style="normal">NotoSansOldPersian-Regular.ttf</font> + <font weight="400" style="normal">NotoSansOldPersian.ttf</font> </family> <family lang="und-Sarb"> - <font weight="400" style="normal">NotoSansOldSouthArabian-Regular.ttf</font> + <font weight="400" style="normal">NotoSansOldSouthArabian.ttf</font> </family> <family lang="und-Orkh"> - <font weight="400" style="normal">NotoSansOldTurkic-Regular.ttf</font> + <font weight="400" style="normal">NotoSansOldTurkic.ttf</font> </family> <family lang="und-Osge"> <font weight="400" style="normal">NotoSansOsage-Regular.ttf</font> </family> <family lang="und-Osma"> - <font weight="400" style="normal">NotoSansOsmanya-Regular.ttf</font> + <font weight="400" style="normal">NotoSansOsmanya.ttf</font> </family> <family lang="und-Phnx"> - <font weight="400" style="normal">NotoSansPhoenician-Regular.ttf</font> + <font weight="400" style="normal">NotoSansPhoenician.ttf</font> </family> <family lang="und-Rjng"> - <font weight="400" style="normal">NotoSansRejang-Regular.ttf</font> + <font weight="400" style="normal">NotoSansRejang.ttf</font> </family> <family lang="und-Runr"> - <font weight="400" style="normal">NotoSansRunic-Regular.ttf</font> + <font weight="400" style="normal">NotoSansRunic.ttf</font> </family> <family lang="und-Samr"> - <font weight="400" style="normal">NotoSansSamaritan-Regular.ttf</font> + <font weight="400" style="normal">NotoSansSamaritan.ttf</font> </family> <family lang="und-Saur"> - <font weight="400" style="normal">NotoSansSaurashtra-Regular.ttf</font> + <font weight="400" style="normal">NotoSansSaurashtra.ttf</font> </family> <family lang="und-Shaw"> - <font weight="400" style="normal">NotoSansShavian-Regular.ttf</font> + <font weight="400" style="normal">NotoSansShavian.ttf</font> </family> <family lang="und-Sund"> - <font weight="400" style="normal">NotoSansSundanese-Regular.ttf</font> + <font weight="400" style="normal">NotoSansSundanese.ttf</font> </family> <family lang="und-Sylo"> - <font weight="400" style="normal">NotoSansSylotiNagri-Regular.ttf</font> + <font weight="400" style="normal">NotoSansSylotiNagri.ttf</font> </family> <!-- Esrangela should precede Eastern and Western Syriac, since it's our default form. --> <family lang="und-Syre"> - <font weight="400" style="normal">NotoSansSyriacEstrangela-Regular.ttf</font> + <font weight="400" style="normal">NotoSansSyriacEstrangela.ttf</font> </family> <family lang="und-Syrn"> - <font weight="400" style="normal">NotoSansSyriacEastern-Regular.ttf</font> + <font weight="400" style="normal">NotoSansSyriacEastern.ttf</font> </family> <family lang="und-Syrj"> - <font weight="400" style="normal">NotoSansSyriacWestern-Regular.ttf</font> + <font weight="400" style="normal">NotoSansSyriacWestern.ttf</font> </family> <family lang="und-Tglg"> - <font weight="400" style="normal">NotoSansTagalog-Regular.ttf</font> + <font weight="400" style="normal">NotoSansTagalog.ttf</font> </family> <family lang="und-Tagb"> - <font weight="400" style="normal">NotoSansTagbanwa-Regular.ttf</font> + <font weight="400" style="normal">NotoSansTagbanwa.ttf</font> </family> <family lang="und-Lana"> - <font weight="400" style="normal">NotoSansTaiTham-Regular.ttf</font> + <font weight="400" style="normal">NotoSansTaiTham.ttf</font> </family> <family lang="und-Tavt"> - <font weight="400" style="normal">NotoSansTaiViet-Regular.ttf</font> + <font weight="400" style="normal">NotoSansTaiViet.ttf</font> </family> <family lang="und-Tibt"> - <font weight="400" style="normal">NotoSerifTibetan-VF.ttf + <font weight="400" style="normal">NotoSerifTibetan-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSerifTibetan-VF.ttf + <font weight="500" style="normal">NotoSerifTibetan-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSerifTibetan-VF.ttf + <font weight="600" style="normal">NotoSerifTibetan-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSerifTibetan-VF.ttf + <font weight="700" style="normal">NotoSerifTibetan-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> @@ -1018,29 +1018,29 @@ <font weight="400" style="normal">NotoSansTifinagh-Regular.otf</font> </family> <family lang="und-Ugar"> - <font weight="400" style="normal">NotoSansUgaritic-Regular.ttf</font> + <font weight="400" style="normal">NotoSansUgaritic.ttf</font> </family> <family lang="und-Vaii"> - <font weight="400" style="normal">NotoSansVai-Regular.ttf</font> + <font weight="400" style="normal">NotoSansVai.ttf</font> </family> <family> <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font> </family> <family lang="zh-Hans"> - <font weight="400" style="normal" index="2">NotoSansCJK-Regular.ttc</font> - <font weight="400" style="normal" index="2" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font> + <font weight="400" style="normal" index="2">NotoSansCJKjp-Regular.otc</font> + <font weight="400" style="normal" index="2" fallbackFor="serif">NotoSerifCJKjp-Regular.otc</font> </family> <family lang="zh-Hant,zh-Bopo"> - <font weight="400" style="normal" index="3">NotoSansCJK-Regular.ttc</font> - <font weight="400" style="normal" index="3" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font> + <font weight="400" style="normal" index="3">NotoSansCJKjp-Regular.otc</font> + <font weight="400" style="normal" index="3" fallbackFor="serif">NotoSerifCJKjp-Regular.otc</font> </family> <family lang="ja"> - <font weight="400" style="normal" index="0">NotoSansCJK-Regular.ttc</font> - <font weight="400" style="normal" index="0" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font> + <font weight="400" style="normal" index="0">NotoSansCJKjp-Regular.otc</font> + <font weight="400" style="normal" index="0" fallbackFor="serif">NotoSerifCJKjp-Regular.otc</font> </family> <family lang="ko"> - <font weight="400" style="normal" index="1">NotoSansCJK-Regular.ttc</font> - <font weight="400" style="normal" index="1" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font> + <font weight="400" style="normal" index="1">NotoSansCJKjp-Regular.otc</font> + <font weight="400" style="normal" index="1" fallbackFor="serif">NotoSerifCJKjp-Regular.otc</font> </family> <family lang="und-Zsye"> <font weight="400" style="normal">NotoColorEmoji.ttf</font> @@ -1053,16 +1053,16 @@ override the East Asian punctuation for Chinese. --> <family lang="und-Tale"> - <font weight="400" style="normal">NotoSansTaiLe-Regular.ttf</font> + <font weight="400" style="normal">NotoSansTaiLe.ttf</font> </family> <family lang="und-Yiii"> - <font weight="400" style="normal">NotoSansYi-Regular.ttf</font> + <font weight="400" style="normal">NotoSansYi.ttf</font> </family> <family lang="und-Mong"> - <font weight="400" style="normal">NotoSansMongolian-Regular.ttf</font> + <font weight="400" style="normal">NotoSansMongolian.ttf</font> </family> <family lang="und-Phag"> - <font weight="400" style="normal">NotoSansPhagsPa-Regular.ttf</font> + <font weight="400" style="normal">NotoSansPhagsPa.ttf</font> </family> <family lang="und-Hluw"> <font weight="400" style="normal">NotoSansAnatolianHieroglyphs-Regular.otf</font> @@ -1152,72 +1152,72 @@ <font weight="400" style="normal">NotoSerifDogra-Regular.ttf</font> </family> <family lang="und-Medf"> - <font weight="400" style="normal">NotoSansMedefaidrin-VF.ttf + <font weight="400" style="normal">NotoSansMedefaidrin-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansMedefaidrin-VF.ttf + <font weight="500" style="normal">NotoSansMedefaidrin-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansMedefaidrin-VF.ttf + <font weight="600" style="normal">NotoSansMedefaidrin-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansMedefaidrin-VF.ttf + <font weight="700" style="normal">NotoSansMedefaidrin-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Soyo"> - <font weight="400" style="normal">NotoSansSoyombo-VF.ttf + <font weight="400" style="normal">NotoSansSoyombo-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansSoyombo-VF.ttf + <font weight="500" style="normal">NotoSansSoyombo-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansSoyombo-VF.ttf + <font weight="600" style="normal">NotoSansSoyombo-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansSoyombo-VF.ttf + <font weight="700" style="normal">NotoSansSoyombo-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Takr"> - <font weight="400" style="normal">NotoSansTakri-VF.ttf + <font weight="400" style="normal">NotoSansTakri-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansTakri-VF.ttf + <font weight="500" style="normal">NotoSansTakri-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansTakri-VF.ttf + <font weight="600" style="normal">NotoSansTakri-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansTakri-VF.ttf + <font weight="700" style="normal">NotoSansTakri-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Hmnp"> - <font weight="400" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf + <font weight="400" style="normal">NotoSerifHmongNyiakeng-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf + <font weight="500" style="normal">NotoSerifHmongNyiakeng-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf + <font weight="600" style="normal">NotoSerifHmongNyiakeng-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf + <font weight="700" style="normal">NotoSerifHmongNyiakeng-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Yezi"> - <font weight="400" style="normal">NotoSerifYezidi-VF.ttf + <font weight="400" style="normal">NotoSerifYezidi-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSerifYezidi-VF.ttf + <font weight="500" style="normal">NotoSerifYezidi-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSerifYezidi-VF.ttf + <font weight="600" style="normal">NotoSerifYezidi-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSerifYezidi-VF.ttf + <font weight="700" style="normal">NotoSerifYezidi-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> diff --git a/data/keyboards/OWNERS b/data/keyboards/OWNERS index c4f6df824a39..0ce83507160c 100644 --- a/data/keyboards/OWNERS +++ b/data/keyboards/OWNERS @@ -1,5 +1,3 @@ set noparent -michaelwr@google.com -svv@google.com -lzye@google.com +include /services/core/java/com/android/server/input/OWNERS diff --git a/data/keyboards/Vendor_057e_Product_2009.kl b/data/keyboards/Vendor_057e_Product_2009.kl index 3c6b11e4640c..7491ee562b59 100644 --- a/data/keyboards/Vendor_057e_Product_2009.kl +++ b/data/keyboards/Vendor_057e_Product_2009.kl @@ -74,3 +74,11 @@ key 0x135 BUTTON_MODE # Home key key 0x13c HOME + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/graphics/java/android/graphics/FrameInfo.java b/graphics/java/android/graphics/FrameInfo.java index d59abb5916a0..189be53a397f 100644 --- a/graphics/java/android/graphics/FrameInfo.java +++ b/graphics/java/android/graphics/FrameInfo.java @@ -69,28 +69,26 @@ public final class FrameInfo { // animation & drawing system public static final int VSYNC = 3; - // The time of the oldest input event - public static final int OLDEST_INPUT_EVENT = 4; - - // The time of the newest input event - public static final int NEWEST_INPUT_EVENT = 5; + // The id of the input event that caused the current frame + public static final int INPUT_EVENT_ID = 4; // When input event handling started - public static final int HANDLE_INPUT_START = 6; + public static final int HANDLE_INPUT_START = 5; // When animation evaluations started - public static final int ANIMATION_START = 7; + public static final int ANIMATION_START = 6; // When ViewRootImpl#performTraversals() started - public static final int PERFORM_TRAVERSALS_START = 8; + public static final int PERFORM_TRAVERSALS_START = 7; // When View:draw() started - public static final int DRAW_START = 9; + public static final int DRAW_START = 8; // When the frame needs to be ready by - public static final int FRAME_DEADLINE = 10; + public static final int FRAME_DEADLINE = 9; // Must be the last one + // This value must be in sync with `UI_THREAD_FRAME_INFO_SIZE` in FrameInfo.h private static final int FRAME_INFO_SIZE = FRAME_DEADLINE + 1; /** checkstyle */ diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl index f708298a2cbd..684eebe6ffde 100644 --- a/keystore/java/android/security/IKeyChainService.aidl +++ b/keystore/java/android/security/IKeyChainService.aidl @@ -37,8 +37,6 @@ interface IKeyChainService { void setUserSelectable(String alias, boolean isUserSelectable); int generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec); - int attestKey(in String alias, in byte[] challenge, in int[] idAttestationFlags, - out KeymasterCertificateChain chain); boolean setKeyPairCertificate(String alias, in byte[] userCert, in byte[] certChain); // APIs used by CertInstaller and DevicePolicyManager @@ -65,6 +63,7 @@ interface IKeyChainService { AppUriAuthenticationPolicy getCredentialManagementAppPolicy(); String getPredefinedAliasForPackageAndUri(String packageName, in Uri uri); void removeCredentialManagementApp(); + boolean isCredentialManagementApp(String packageName); // APIs used by KeyChainActivity void setGrant(int uid, String alias, boolean value); diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index 97819c56fd5a..65a81cd57f41 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -44,6 +44,8 @@ import android.os.UserManager; import android.security.keystore.AndroidKeyStoreProvider; import android.security.keystore.KeyPermanentlyInvalidatedException; import android.security.keystore.KeyProperties; +import android.system.keystore2.Domain; +import android.system.keystore2.KeyDescriptor; import android.util.Log; import com.android.org.conscrypt.TrustedCertificateStore; @@ -421,6 +423,15 @@ public final class KeyChain { * credentials. This is limited to unmanaged devices. The authentication policy must be * provided to be able to make this request successfully. * + * <p> This intent should be started using {@link Activity#startActivityForResult(Intent, int)} + * to verify whether the request was successful and whether the user accepted or denied the + * request. If the user successfully receives and accepts the request, the result code will be + * {@link Activity#RESULT_OK}, otherwise the result code will be + * {@link Activity#RESULT_CANCELED}. + * + * <p> {@link KeyChain#isCredentialManagementApp(Context)} should be used to determine whether + * an app is already the credential management app. + * * @param policy The authentication policy determines which alias for a private key and * certificate pair should be used for authentication. */ @@ -589,6 +600,55 @@ public final class KeyChain { } /** + * Check whether the caller is the credential management app {@link CredentialManagementApp}. + * The credential management app has the ability to manage the user's KeyChain credentials + * on unmanaged devices. + * + * <p> {@link KeyChain#createManageCredentialsIntent} should be used by an app to request to + * become the credential management app. The user must approve this request before the app can + * manage the user's credentials. There can only be one credential management on the device. + * + * @return {@code true} if the caller is the credential management app. + */ + public static boolean isCredentialManagementApp(@NonNull Context context) { + boolean isCredentialManagementApp = false; + try (KeyChainConnection keyChainConnection = KeyChain.bind(context)) { + isCredentialManagementApp = keyChainConnection.getService() + .isCredentialManagementApp(context.getPackageName()); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted while checking whether the caller is the " + + "credential management app.", e); + } catch (SecurityException e) { + isCredentialManagementApp = false; + } + return isCredentialManagementApp; + } + + /** + * Called by the credential management app to get the authentication policy + * {@link AppUriAuthenticationPolicy}. + * + * @return the credential management app's authentication policy. + * @throws SecurityException if the caller is not the credential management app. + */ + @NonNull + public static AppUriAuthenticationPolicy getCredentialManagementAppPolicy( + @NonNull Context context) throws SecurityException { + AppUriAuthenticationPolicy policy = null; + try (KeyChainConnection keyChainConnection = KeyChain.bind(context)) { + policy = keyChainConnection.getService().getCredentialManagementAppPolicy(); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } catch (InterruptedException e) { + throw new RuntimeException( + "Interrupted while getting credential management app policy.", e); + } + return policy; + } + + /** * Set a credential management app. The credential management app has the ability to manage * the user's KeyChain credentials on unmanaged devices. * @@ -682,6 +742,33 @@ public final class KeyChain { return null; } + /** + * This prefix is used to disambiguate grant aliase strings from normal key alias strings. + * Technically, a key alias string can use the same prefix. However, a collision does not + * lead to privilege escalation, because grants are access controlled in the Keystore daemon. + * @hide + */ + public static final String GRANT_ALIAS_PREFIX = "ks2_keychain_grant_id:"; + + private static KeyDescriptor getGrantDescriptor(String keyid) { + KeyDescriptor result = new KeyDescriptor(); + result.domain = Domain.GRANT; + result.blob = null; + result.alias = null; + try { + result.nspace = Long.parseUnsignedLong( + keyid.substring(GRANT_ALIAS_PREFIX.length()), 16 /* radix */); + } catch (NumberFormatException e) { + return null; + } + return result; + } + + /** @hide */ + public static String getGrantString(KeyDescriptor key) { + return String.format(GRANT_ALIAS_PREFIX + "%016X", key.nspace); + } + /** @hide */ @Nullable @WorkerThread public static KeyPair getKeyPair(@NonNull Context context, @NonNull String alias) @@ -705,11 +792,23 @@ public final class KeyChain { if (keyId == null) { return null; + } + + if (AndroidKeyStoreProvider.isKeystore2Enabled()) { + try { + return android.security.keystore2.AndroidKeyStoreProvider + .loadAndroidKeyStoreKeyPairFromKeystore( + KeyStore2.getInstance(), + getGrantDescriptor(keyId)); + } catch (UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) { + throw new KeyChainException(e); + } } else { try { return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore( KeyStore.getInstance(), keyId, KeyStore.UID_SELF); - } catch (RuntimeException | UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) { + } catch (RuntimeException | UnrecoverableKeyException + | KeyPermanentlyInvalidatedException e) { throw new KeyChainException(e); } } @@ -827,11 +926,8 @@ public final class KeyChain { @Deprecated public static boolean isBoundKeyAlgorithm( @NonNull @KeyProperties.KeyAlgorithmEnum String algorithm) { - if (!isKeyAlgorithmSupported(algorithm)) { - return false; - } - - return KeyStore.getInstance().isHardwareBacked(algorithm); + // All supported algorithms are hardware backed. Individual keys may not be. + return true; } /** @hide */ diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java index c20cf01a993e..a6e33664f2b1 100644 --- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java @@ -59,7 +59,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeString(mSpec.getKeystoreAlias()); out.writeInt(mSpec.getPurposes()); - out.writeInt(mSpec.getUid()); + out.writeInt(mSpec.getNamespace()); out.writeInt(mSpec.getKeySize()); // Only needs to support RSAKeyGenParameterSpec and ECGenParameterSpec. @@ -125,7 +125,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { private ParcelableKeyGenParameterSpec(Parcel in) { final String keystoreAlias = in.readString(); final int purposes = in.readInt(); - final int uid = in.readInt(); + final int namespace = in.readInt(); final int keySize = in.readInt(); final int keySpecType = in.readInt(); @@ -177,7 +177,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { // KeyGenParameterSpec constructor (whereas using a builder would silently drop them). mSpec = new KeyGenParameterSpec( keystoreAlias, - uid, + namespace, keySize, algorithmSpec, certificateSubject, diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java index b3bfd6a3a97a..e401add9ece7 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -154,7 +154,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato private KeyGenParameterSpec mSpec; private String mEntryAlias; - private int mEntryUid; + private int mEntryNamespace; private @KeyProperties.KeyAlgorithmEnum String mJcaKeyAlgorithm; private int mKeymasterAlgorithm = -1; private int mKeySizeBits; @@ -218,7 +218,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato } mEntryAlias = spec.getKeystoreAlias(); - mEntryUid = spec.getUid(); + mEntryNamespace = spec.getNamespace(); mSpec = spec; mKeymasterAlgorithm = keymasterAlgorithm; mKeySizeBits = spec.getKeySize(); @@ -439,7 +439,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato private void resetAll() { mEntryAlias = null; - mEntryUid = KeyProperties.NAMESPACE_APPLICATION; + mEntryNamespace = KeyProperties.NAMESPACE_APPLICATION; mJcaKeyAlgorithm = null; mKeymasterAlgorithm = -1; mKeymasterPurposes = null; @@ -541,10 +541,10 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato KeyDescriptor descriptor = new KeyDescriptor(); descriptor.alias = mEntryAlias; - descriptor.domain = mEntryUid == KeyProperties.NAMESPACE_APPLICATION + descriptor.domain = mEntryNamespace == KeyProperties.NAMESPACE_APPLICATION ? Domain.APP : Domain.SELINUX; - descriptor.nspace = mEntryUid; + descriptor.nspace = mEntryNamespace; descriptor.blob = null; boolean success = false; diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java index e1011155248e..35059ac929c3 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java @@ -273,10 +273,10 @@ public class AndroidKeyStoreProvider extends Provider { /** @hide **/ @NonNull public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore( - @NonNull KeyStore2 keyStore, @NonNull String privateKeyAlias, int namespace) + @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor) throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { AndroidKeyStoreKey key = - loadAndroidKeyStoreKeyFromKeystore(keyStore, privateKeyAlias, namespace); + loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor); if (key instanceof AndroidKeyStorePublicKey) { AndroidKeyStorePublicKey publicKey = (AndroidKeyStorePublicKey) key; return new KeyPair(publicKey, publicKey.getPrivateKey()); @@ -336,7 +336,7 @@ public class AndroidKeyStoreProvider extends Provider { @NonNull public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore( @NonNull KeyStore2 keyStore, @NonNull String alias, int namespace) - throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { + throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { KeyDescriptor descriptor = new KeyDescriptor(); if (namespace == KeyProperties.NAMESPACE_APPLICATION) { @@ -348,6 +348,18 @@ public class AndroidKeyStoreProvider extends Provider { } descriptor.alias = alias; descriptor.blob = null; + + final AndroidKeyStoreKey key = loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor); + if (key instanceof AndroidKeyStorePublicKey) { + return ((AndroidKeyStorePublicKey) key).getPrivateKey(); + } else { + return key; + } + } + + private static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore( + @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor) + throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { KeyEntryResponse response = null; try { response = keyStore.getKeyEntry(descriptor); @@ -397,7 +409,7 @@ public class AndroidKeyStoreProvider extends Provider { keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) { return makeAndroidKeyStorePublicKeyFromKeyEntryResponse(descriptor, response.metadata, new KeyStoreSecurityLevel(response.iSecurityLevel), - keymasterAlgorithm).getPrivateKey(); + keymasterAlgorithm); } else { throw new UnrecoverableKeyException("Key algorithm unknown"); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java index 59271e9fb63c..e6e6d4a1934f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java @@ -22,7 +22,7 @@ import android.os.RemoteException; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.pip.PinnedStackListenerForwarder; -import com.android.wm.shell.pip.PinnedStackListenerForwarder.PinnedStackListener; +import com.android.wm.shell.pip.PinnedStackListenerForwarder.PinnedTaskListener; /** * The singleton wrapper to communicate between WindowManagerService and WMShell features @@ -46,7 +46,7 @@ public class WindowManagerShellWrapper { * Adds a pinned stack listener, which will receive updates from the window manager service * along with any other pinned stack listeners that were added via this method. */ - public void addPinnedStackListener(PinnedStackListener listener) + public void addPinnedStackListener(PinnedTaskListener listener) throws RemoteException { mPinnedStackListenerForwarder.addListener(listener); mPinnedStackListenerForwarder.register(DEFAULT_DISPLAY); @@ -55,7 +55,7 @@ public class WindowManagerShellWrapper { /** * Removes a pinned stack listener. */ - public void removePinnedStackListener(PinnedStackListener listener) { + public void removePinnedStackListener(PinnedTaskListener listener) { mPinnedStackListenerForwarder.removeListener(listener); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java index 79f9dcd8a1fb..562b32b41dd2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java @@ -34,6 +34,7 @@ import androidx.annotation.Nullable; import com.android.internal.protolog.common.ProtoLog; 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.SyncTransactionQueue; import com.android.wm.shell.common.split.SplitLayout; @@ -57,12 +58,14 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan private final AppPairsController mController; private final SyncTransactionQueue mSyncQueue; private final DisplayController mDisplayController; + private final DisplayImeController mDisplayImeController; private SplitLayout mSplitLayout; AppPair(AppPairsController controller) { mController = controller; mSyncQueue = controller.getSyncTransactionQueue(); mDisplayController = controller.getDisplayController(); + mDisplayImeController = controller.getDisplayImeController(); } int getRootTaskId() { @@ -97,7 +100,7 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan mSplitLayout = new SplitLayout(TAG + "SplitDivider", mDisplayController.getDisplayContext(mRootTaskInfo.displayId), mRootTaskInfo.configuration, this /* layoutChangeListener */, - b -> b.setParent(mRootTaskLeash)); + b -> b.setParent(mRootTaskLeash), mDisplayImeController); final WindowContainerToken token1 = task1.token; final WindowContainerToken token2 = task2.token; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java index 0415f12496f2..b159333e9a0e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java @@ -23,17 +23,16 @@ import android.util.Slog; import android.util.SparseArray; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; 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 com.android.wm.shell.common.SyncTransactionQueue; import java.io.PrintWriter; -import java.util.concurrent.TimeUnit; /** * Class manages app-pairs multitasking mode and implements the main interface {@link AppPairs}. @@ -50,12 +49,15 @@ public class AppPairsController { // Active app-pairs mapped by root task id key. private final SparseArray<AppPair> mActiveAppPairs = new SparseArray<>(); private final DisplayController mDisplayController; + private final DisplayImeController mDisplayImeController; public AppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue, - DisplayController displayController, ShellExecutor mainExecutor) { + DisplayController displayController, ShellExecutor mainExecutor, + DisplayImeController displayImeController) { mTaskOrganizer = organizer; mSyncQueue = syncQueue; mDisplayController = displayController; + mDisplayImeController = displayImeController; mMainExecutor = mainExecutor; } @@ -130,18 +132,22 @@ public class AppPairsController { } } - public ShellTaskOrganizer getTaskOrganizer() { + ShellTaskOrganizer getTaskOrganizer() { return mTaskOrganizer; } - public SyncTransactionQueue getSyncTransactionQueue() { + SyncTransactionQueue getSyncTransactionQueue() { return mSyncQueue; } - public DisplayController getDisplayController() { + DisplayController getDisplayController() { return mDisplayController; } + DisplayImeController getDisplayImeController() { + return mDisplayImeController; + } + public void dump(@NonNull PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; final String childPrefix = innerPrefix + " "; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index 0ee1f0642352..8697be9db3fd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -124,6 +124,7 @@ public class Bubble implements BubbleViewProvider { private int mDesiredHeight; @DimenRes private int mDesiredHeightResId; + private int mTaskId; /** for logging **/ @Nullable @@ -162,7 +163,7 @@ public class Bubble implements BubbleViewProvider { */ Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo, final int desiredHeight, final int desiredHeightResId, @Nullable final String title, - Executor mainExecutor) { + int taskId, Executor mainExecutor) { Objects.requireNonNull(key); Objects.requireNonNull(shortcutInfo); mMetadataShortcutId = shortcutInfo.getId(); @@ -178,6 +179,7 @@ public class Bubble implements BubbleViewProvider { mTitle = title; mShowBubbleUpdateDot = false; mMainExecutor = mainExecutor; + mTaskId = taskId; } @VisibleForTesting(visibility = PRIVATE) @@ -197,6 +199,7 @@ public class Bubble implements BubbleViewProvider { }); }; mMainExecutor = mainExecutor; + mTaskId = INVALID_TASK_ID; setEntry(entry); } @@ -520,7 +523,7 @@ public class Bubble implements BubbleViewProvider { */ @Override public int getTaskId() { - return mExpandedView != null ? mExpandedView.getTaskId() : INVALID_TASK_ID; + return mExpandedView != null ? mExpandedView.getTaskId() : mTaskId; } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 047df5ba7ca9..1320780bfb8f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -1213,7 +1213,7 @@ public class BubbleController { /** PinnedStackListener that dispatches IME visibility updates to the stack. */ //TODO(b/170442945): Better way to do this / insets listener? - private class BubblesImeListener extends PinnedStackListenerForwarder.PinnedStackListener { + private class BubblesImeListener extends PinnedStackListenerForwarder.PinnedTaskListener { @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { if (mStackView != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt index 3108b02cc010..241755227af0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt @@ -28,7 +28,6 @@ import com.android.wm.shell.bubbles.storage.BubbleEntity import com.android.wm.shell.bubbles.storage.BubblePersistentRepository import com.android.wm.shell.bubbles.storage.BubbleVolatileRepository import com.android.wm.shell.common.ShellExecutor -import com.android.wm.shell.common.annotations.ExternalThread import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -36,8 +35,11 @@ import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.launch import kotlinx.coroutines.yield -internal class BubbleDataRepository(context: Context, private val launcherApps: LauncherApps, - private val mainExecutor : ShellExecutor) { +internal class BubbleDataRepository( + context: Context, + private val launcherApps: LauncherApps, + private val mainExecutor: ShellExecutor +) { private val volatileRepository = BubbleVolatileRepository(launcherApps) private val persistentRepository = BubblePersistentRepository(context) @@ -78,7 +80,8 @@ internal class BubbleDataRepository(context: Context, private val launcherApps: b.key, b.rawDesiredHeight, b.rawDesiredHeightResId, - b.title + b.title, + b.taskId ) } } @@ -168,6 +171,7 @@ internal class BubbleDataRepository(context: Context, private val launcherApps: entity.desiredHeight, entity.desiredHeightResId, entity.title, + entity.taskId, mainExecutor ) } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index a3edc20e242a..a8ab4064455c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -1539,19 +1539,16 @@ public class BubbleStackView extends FrameLayout * Update bubble order and pointer position. */ public void updateBubbleOrder(List<Bubble> bubbles) { - if (isExpansionAnimating()) { - return; - } final Runnable reorder = () -> { for (int i = 0; i < bubbles.size(); i++) { Bubble bubble = bubbles.get(i); mBubbleContainer.reorderView(bubble.getIconView(), i); } }; - if (mIsExpanded) { + if (mIsExpanded || isExpansionAnimating()) { reorder.run(); updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */); - } else { + } else if (!isExpansionAnimating()) { List<View> bubbleViews = bubbles.stream() .map(b -> b.getIconView()).collect(Collectors.toList()); mStackAnimationController.animateReorder(bubbleViews, reorder); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt index aeba302bf487..d5cab5af42e4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt @@ -25,5 +25,6 @@ data class BubbleEntity( val key: String, val desiredHeight: Int, @DimenRes val desiredHeightResId: Int, - val title: String? = null + val title: String? = null, + val taskId: Int ) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt index fe72bd301e04..470011b136fe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt @@ -15,6 +15,7 @@ */ package com.android.wm.shell.bubbles.storage +import android.app.ActivityTaskManager.INVALID_TASK_ID import android.util.Xml import com.android.internal.util.FastXmlSerializer import com.android.internal.util.XmlUtils @@ -38,6 +39,7 @@ private const val ATTR_KEY = "key" private const val ATTR_DESIRED_HEIGHT = "h" private const val ATTR_DESIRED_HEIGHT_RES_ID = "hid" private const val ATTR_TITLE = "t" +private const val ATTR_TASK_ID = "tid" /** * Writes the bubbles in xml format into given output stream. @@ -70,6 +72,7 @@ private fun writeXmlEntry(serializer: XmlSerializer, bubble: BubbleEntity) { serializer.attribute(null, ATTR_DESIRED_HEIGHT, bubble.desiredHeight.toString()) serializer.attribute(null, ATTR_DESIRED_HEIGHT_RES_ID, bubble.desiredHeightResId.toString()) bubble.title?.let { serializer.attribute(null, ATTR_TITLE, it) } + serializer.attribute(null, ATTR_TASK_ID, bubble.taskId.toString()) serializer.endTag(null, TAG_BUBBLE) } catch (e: IOException) { throw RuntimeException(e) @@ -103,7 +106,8 @@ private fun readXmlEntry(parser: XmlPullParser): BubbleEntity? { parser.getAttributeWithName(ATTR_KEY) ?: return null, parser.getAttributeWithName(ATTR_DESIRED_HEIGHT)?.toInt() ?: return null, parser.getAttributeWithName(ATTR_DESIRED_HEIGHT_RES_ID)?.toInt() ?: return null, - parser.getAttributeWithName(ATTR_TITLE) + parser.getAttributeWithName(ATTR_TITLE), + parser.getAttributeWithName(ATTR_TASK_ID)?.toInt() ?: INVALID_TASK_ID ) } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java index 58a4baf39614..4564af438f32 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java @@ -22,6 +22,8 @@ import static android.content.res.Configuration.UI_MODE_TYPE_CAR; import static android.content.res.Configuration.UI_MODE_TYPE_MASK; import static android.os.Process.SYSTEM_UID; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS; +import static android.util.RotationUtils.rotateBounds; +import static android.util.RotationUtils.rotateInsets; import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_270; @@ -37,7 +39,6 @@ import android.graphics.Rect; import android.os.SystemProperties; import android.provider.Settings; import android.util.DisplayMetrics; -import android.util.RotationUtils; import android.util.Size; import android.view.Display; import android.view.DisplayCutout; @@ -241,38 +242,6 @@ public class DisplayLayout { } /** - * Rotates bounds as if parentBounds and bounds are a group. The group is rotated by `delta` - * 90-degree counter-clockwise increments. This assumes that parentBounds is at 0,0 and - * remains at 0,0 after rotation. - * - * Only 'bounds' is mutated. - */ - public static void rotateBounds(Rect inOutBounds, Rect parentBounds, int delta) { - int rdelta = ((delta % 4) + 4) % 4; - int origLeft = inOutBounds.left; - switch (rdelta) { - case 0: - return; - case 1: - inOutBounds.left = inOutBounds.top; - inOutBounds.top = parentBounds.right - inOutBounds.right; - inOutBounds.right = inOutBounds.bottom; - inOutBounds.bottom = parentBounds.right - origLeft; - return; - case 2: - inOutBounds.left = parentBounds.right - inOutBounds.right; - inOutBounds.right = parentBounds.right - origLeft; - return; - case 3: - inOutBounds.left = parentBounds.bottom - inOutBounds.bottom; - inOutBounds.bottom = inOutBounds.right; - inOutBounds.right = parentBounds.bottom - inOutBounds.top; - inOutBounds.top = origLeft; - return; - } - } - - /** * Calculates the stable insets if we already have the non-decor insets. */ private static void convertNonDecorInsetsToStableInsets(Resources res, Rect inOutInsets, @@ -359,8 +328,7 @@ public class DisplayLayout { if (rotation == ROTATION_0) { return computeSafeInsets(cutout, displayWidth, displayHeight); } - final Insets waterfallInsets = - RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation); + final Insets waterfallInsets = rotateInsets(cutout.getWaterfallInsets(), rotation); final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); Rect[] cutoutRects = cutout.getBoundingRectsAll(); final Rect[] newBounds = new Rect[cutoutRects.length]; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java index e94080aa8db7..3b670057cb1a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java @@ -227,7 +227,7 @@ public class TaskStackListenerImpl extends TaskStackListener implements Handler. } @Override - public void onActivityDismissingDockedStack() { + public void onActivityDismissingDockedTask() { mMainHandler.sendEmptyMessage(ON_ACTIVITY_DISMISSING_DOCKED_STACK); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java index c27c92961c2b..b9fdaa1ab1af 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java @@ -36,11 +36,13 @@ import androidx.annotation.Nullable; import com.android.internal.policy.DividerSnapAlgorithm; import com.android.wm.shell.R; import com.android.wm.shell.animation.Interpolators; +import com.android.wm.shell.common.DisplayImeController; /** - * Stack divider for app pair. + * Divider for multi window splits. */ -public class DividerView extends FrameLayout implements View.OnTouchListener { +public class DividerView extends FrameLayout implements View.OnTouchListener, + DisplayImeController.ImePositionProcessor { public static final long TOUCH_ANIMATION_DURATION = 150; public static final long TOUCH_RELEASE_ANIMATION_DURATION = 200; @@ -56,6 +58,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { private boolean mMoving; private int mStartPos; private GestureDetector mDoubleTapDetector; + private boolean mInteractive; public DividerView(@NonNull Context context) { super(context); @@ -91,12 +94,19 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { mTouchElevation = getResources().getDimensionPixelSize( R.dimen.docked_stack_divider_lift_elevation); mDoubleTapDetector = new GestureDetector(getContext(), new DoubleTapListener()); + mInteractive = true; setOnTouchListener(this); } @Override + public void onImeVisibilityChanged(int displayId, boolean isShowing) { + if (displayId != getDisplay().getDisplayId()) return; + setInteractive(!isShowing); + } + + @Override public boolean onTouch(View v, MotionEvent event) { - if (mSplitLayout == null) { + if (mSplitLayout == null || !mInteractive) { return false; } @@ -202,6 +212,13 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { mViewHost.relayout(lp); } + private void setInteractive(boolean interactive) { + if (interactive == mInteractive) return; + mInteractive = interactive; + releaseTouching(); + mHandle.setVisibility(mInteractive ? View.VISIBLE : View.INVISIBLE); + } + private boolean isLandscape() { return getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index 60231df37370..bacff78e6a67 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -35,6 +35,7 @@ import androidx.annotation.Nullable; import com.android.internal.policy.DividerSnapAlgorithm; import com.android.wm.shell.animation.Interpolators; +import com.android.wm.shell.common.DisplayImeController; /** * Records and handles layout of splits. Helps to calculate proper bounds when configuration or @@ -59,11 +60,13 @@ public class SplitLayout { public SplitLayout(String windowName, Context context, Configuration configuration, LayoutChangeListener layoutChangeListener, - SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks) { + SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks, + DisplayImeController displayImeController) { mContext = context.createConfigurationContext(configuration); mLayoutChangeListener = layoutChangeListener; mSplitWindowManager = new SplitWindowManager( - windowName, mContext, configuration, parentContainerCallbacks); + windowName, mContext, configuration, parentContainerCallbacks, + displayImeController); mDividerWindowWidth = context.getResources().getDimensionPixelSize( com.android.internal.R.dimen.docked_stack_divider_thickness); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java index 87f0c25c93df..f6efb0120dda 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java @@ -46,6 +46,7 @@ import android.view.WindowlessWindowManager; import androidx.annotation.Nullable; import com.android.wm.shell.R; +import com.android.wm.shell.common.DisplayImeController; /** * Holds view hierarchy of a root surface and helps to inflate {@link DividerView} for a split. @@ -53,23 +54,27 @@ import com.android.wm.shell.R; public final class SplitWindowManager extends WindowlessWindowManager { private static final String TAG = SplitWindowManager.class.getSimpleName(); + private final String mWindowName; + private final DisplayImeController mDisplayImeController; private final ParentContainerCallbacks mParentContainerCallbacks; private Context mContext; private SurfaceControlViewHost mViewHost; private SurfaceControl mLeash; private boolean mResizingSplits; - private final String mWindowName; + private DividerView mDividerView; public interface ParentContainerCallbacks { void attachToParentSurface(SurfaceControl.Builder b); } public SplitWindowManager(String windowName, Context context, Configuration config, - ParentContainerCallbacks parentContainerCallbacks) { + ParentContainerCallbacks parentContainerCallbacks, + DisplayImeController displayImeController) { super(config, null /* rootSurface */, null /* hostInputToken */); mContext = context.createConfigurationContext(config); mParentContainerCallbacks = parentContainerCallbacks; mWindowName = windowName; + mDisplayImeController = displayImeController; } @Override @@ -103,14 +108,16 @@ public final class SplitWindowManager extends WindowlessWindowManager { /** Inflates {@link DividerView} on to the root surface. */ void init(SplitLayout splitLayout) { - if (mViewHost == null) { - mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); + if (mDividerView != null || mViewHost != null) { + throw new UnsupportedOperationException( + "Try to inflate divider view again without release first"); } - final Rect dividerBounds = splitLayout.getDividerBounds(); - final DividerView dividerView = (DividerView) LayoutInflater.from(mContext) + mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); + mDividerView = (DividerView) LayoutInflater.from(mContext) .inflate(R.layout.split_divider, null /* root */); + final Rect dividerBounds = splitLayout.getDividerBounds(); WindowManager.LayoutParams lp = new WindowManager.LayoutParams( dividerBounds.width(), dividerBounds.height(), TYPE_DOCK_DIVIDER, FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL | FLAG_WATCH_OUTSIDE_TOUCH @@ -119,8 +126,9 @@ public final class SplitWindowManager extends WindowlessWindowManager { lp.token = new Binder(); lp.setTitle(mWindowName); lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; - mViewHost.setView(dividerView, lp); - dividerView.setup(splitLayout, mViewHost); + mViewHost.setView(mDividerView, lp); + mDividerView.setup(splitLayout, mViewHost); + mDisplayImeController.addPositionProcessor(mDividerView); } /** @@ -128,6 +136,11 @@ public final class SplitWindowManager extends WindowlessWindowManager { * hierarchy. */ void release() { + if (mDividerView != null) { + mDisplayImeController.removePositionProcessor(mDividerView); + mDividerView = null; + } + if (mViewHost != null){ mViewHost.release(); mViewHost = null; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java index 477ec339f1db..40244fbb4503 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java @@ -18,6 +18,7 @@ package com.android.wm.shell.legacysplitscreen; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.util.RotationUtils.rotateBounds; import static android.view.WindowManager.DOCKED_BOTTOM; import static android.view.WindowManager.DOCKED_INVALID; import static android.view.WindowManager.DOCKED_LEFT; @@ -244,7 +245,7 @@ public class LegacySplitDisplayLayout { DividerSnapAlgorithm snap = initSnapAlgorithmForRotation(context, tmpDL, dividerSize); tmpRect.set(bounds); - DisplayLayout.rotateBounds(tmpRect, displayRect, rotation - dl.rotation()); + rotateBounds(tmpRect, displayRect, dl.rotation(), rotation); rotatedDisplayRect.set(0, 0, tmpDL.width(), tmpDL.height()); final int dockSide = getPrimarySplitSide(tmpRect, rotatedDisplayRect, tmpDL.getOrientation()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java index c8f89876222e..82468ad999b4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java @@ -105,7 +105,7 @@ class WindowManagerProxy { synchronized (mDockedRect) { mTouchableRegion.set(region); } - WindowManagerGlobal.getWindowManagerService().setDockedStackDividerTouchRegion( + WindowManagerGlobal.getWindowManagerService().setDockedTaskDividerTouchRegion( mTouchableRegion); } catch (RemoteException e) { Log.w(TAG, "Failed to set touchable region: " + e); 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 afc8a097dd05..4c5cc226b40f 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 @@ -202,6 +202,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { animateWindows(token, leash, fromBounds, toBounds, direction, mEnterExitAnimationDurationMs); wct.setBounds(token, toBounds); + wct.setAppBounds(token, toBounds); }); applyTransaction(wct); } @@ -231,6 +232,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { // DisplayRotationController will applyTransaction() after finish rotating if (wct != null) { wct.setBounds(token, null/* reset */); + wct.setAppBounds(token, null/* reset */); } }); tx.apply(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java index 8f8ec475a85c..b3b1ba7cd1c1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java @@ -20,7 +20,7 @@ import android.app.RemoteAction; import android.content.ComponentName; import android.content.pm.ParceledListSlice; import android.os.RemoteException; -import android.view.IPinnedStackListener; +import android.view.IPinnedTaskListener; import android.view.WindowManagerGlobal; import androidx.annotation.BinderThread; @@ -32,66 +32,66 @@ import java.util.ArrayList; /** * PinnedStackListener that simply forwards all calls to each listener added via * {@link #addListener}. This is necessary since calling - * {@link com.android.server.wm.WindowManagerService#registerPinnedStackListener} replaces any + * {@link com.android.server.wm.WindowManagerService#registerPinnedTaskListener} replaces any * previously set listener. */ public class PinnedStackListenerForwarder { - private final IPinnedStackListener mListenerImpl = new PinnedStackListenerImpl(); + private final IPinnedTaskListener mListenerImpl = new PinnedTaskListenerImpl(); private final ShellExecutor mMainExecutor; - private final ArrayList<PinnedStackListener> mListeners = new ArrayList<>(); + private final ArrayList<PinnedTaskListener> mListeners = new ArrayList<>(); public PinnedStackListenerForwarder(ShellExecutor mainExecutor) { mMainExecutor = mainExecutor; } /** Adds a listener to receive updates from the WindowManagerService. */ - public void addListener(PinnedStackListener listener) { + public void addListener(PinnedTaskListener listener) { mListeners.add(listener); } /** Removes a listener so it will no longer receive updates from the WindowManagerService. */ - public void removeListener(PinnedStackListener listener) { + public void removeListener(PinnedTaskListener listener) { mListeners.remove(listener); } public void register(int displayId) throws RemoteException { - WindowManagerGlobal.getWindowManagerService().registerPinnedStackListener( + WindowManagerGlobal.getWindowManagerService().registerPinnedTaskListener( displayId, mListenerImpl); } private void onMovementBoundsChanged(boolean fromImeAdjustment) { - for (PinnedStackListener listener : mListeners) { + for (PinnedTaskListener listener : mListeners) { listener.onMovementBoundsChanged(fromImeAdjustment); } } private void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { - for (PinnedStackListener listener : mListeners) { + for (PinnedTaskListener listener : mListeners) { listener.onImeVisibilityChanged(imeVisible, imeHeight); } } private void onActionsChanged(ParceledListSlice<RemoteAction> actions) { - for (PinnedStackListener listener : mListeners) { + for (PinnedTaskListener listener : mListeners) { listener.onActionsChanged(actions); } } private void onActivityHidden(ComponentName componentName) { - for (PinnedStackListener listener : mListeners) { + for (PinnedTaskListener listener : mListeners) { listener.onActivityHidden(componentName); } } private void onAspectRatioChanged(float aspectRatio) { - for (PinnedStackListener listener : mListeners) { + for (PinnedTaskListener listener : mListeners) { listener.onAspectRatioChanged(aspectRatio); } } @BinderThread - private class PinnedStackListenerImpl extends IPinnedStackListener.Stub { + private class PinnedTaskListenerImpl extends IPinnedTaskListener.Stub { @Override public void onMovementBoundsChanged(boolean fromImeAdjustment) { mMainExecutor.execute(() -> { @@ -129,10 +129,10 @@ public class PinnedStackListenerForwarder { } /** - * A counterpart of {@link IPinnedStackListener} with empty implementations. + * A counterpart of {@link IPinnedTaskListener} with empty implementations. * Subclasses can ignore those methods they do not intend to take action upon. */ - public static class PinnedStackListener { + public static class PinnedTaskListener { public void onMovementBoundsChanged(boolean fromImeAdjustment) {} public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index 5ffa9885a143..66560d339c5a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -16,6 +16,7 @@ package com.android.wm.shell.pip; +import static android.util.RotationUtils.rotateBounds; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; @@ -33,7 +34,6 @@ import android.view.SurfaceControl; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.wm.shell.animation.Interpolators; -import com.android.wm.shell.common.DisplayLayout; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -448,7 +448,7 @@ public class PipAnimationController { // Rotate the end bounds according to the rotation delta because the display will // be rotated to the same orientation. rotatedEndRect = new Rect(endValue); - DisplayLayout.rotateBounds(rotatedEndRect, endValue, rotationDelta); + rotateBounds(rotatedEndRect, endValue, rotationDelta); } else { rotatedEndRect = null; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java index 1a4616c5f591..6afcc06aa1ae 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java @@ -32,6 +32,7 @@ import android.media.session.MediaController; import android.media.session.MediaSessionManager; import android.media.session.PlaybackState; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.UserHandle; import androidx.annotation.Nullable; @@ -76,6 +77,7 @@ public class PipMediaController { private final Context mContext; private final Handler mMainHandler; + private final HandlerExecutor mHandlerExecutor; private final MediaSessionManager mMediaSessionManager; private MediaController mMediaController; @@ -123,6 +125,7 @@ public class PipMediaController { public PipMediaController(Context context, Handler mainHandler) { mContext = context; mMainHandler = mainHandler; + mHandlerExecutor = new HandlerExecutor(mMainHandler); IntentFilter mediaControlFilter = new IntentFilter(); mediaControlFilter.addAction(ACTION_PLAY); mediaControlFilter.addAction(ACTION_PAUSE); @@ -247,8 +250,8 @@ public class PipMediaController { */ public void registerSessionListenerForCurrentUser() { mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionsChangedListener); - mMediaSessionManager.addOnActiveSessionsChangedListener(mSessionsChangedListener, null, - UserHandle.CURRENT, mMainHandler); + mMediaSessionManager.addOnActiveSessionsChangedListener(null, UserHandle.CURRENT, + mHandlerExecutor, mSessionsChangedListener); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 4a2a032d8d1c..7fff37d082b1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -93,8 +93,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb protected PhonePipMenuController mMenuController; protected PipTaskOrganizer mPipTaskOrganizer; - protected PinnedStackListenerForwarder.PinnedStackListener mPinnedStackListener = - new PipControllerPinnedStackListener(); + protected PinnedStackListenerForwarder.PinnedTaskListener mPinnedTaskListener = + new PipControllerPinnedTaskListener(); /** * Handler for display rotation changes. @@ -178,8 +178,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb /** * Handler for messages from the PIP controller. */ - private class PipControllerPinnedStackListener extends - PinnedStackListenerForwarder.PinnedStackListener { + private class PipControllerPinnedTaskListener extends + PinnedStackListenerForwarder.PinnedTaskListener { @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { mPipBoundsState.setImeVisibility(imeVisible, imeHeight); @@ -310,7 +310,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb mPipBoundsState.setDisplayLayout(new DisplayLayout(context, context.getDisplay())); try { - mWindowManagerShellWrapper.addPinnedStackListener(mPinnedStackListener); + mWindowManagerShellWrapper.addPinnedStackListener(mPinnedTaskListener); } catch (RemoteException e) { Slog.e(TAG, "Failed to register pinned stack listener", e); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java index 56f183fd7303..70980191f103 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java @@ -377,7 +377,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal private void registerWmShellPinnedStackListener(WindowManagerShellWrapper wmShell) { try { - wmShell.addPinnedStackListener(new PinnedStackListenerForwarder.PinnedStackListener() { + wmShell.addPinnedStackListener(new PinnedStackListenerForwarder.PinnedTaskListener() { @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { if (DEBUG) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index b0167afa2e4e..11548adaf5d1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -42,6 +42,7 @@ import androidx.annotation.Nullable; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.draganddrop.DragAndDropPolicy; @@ -62,18 +63,20 @@ public class SplitScreenController implements DragAndDropPolicy.Starter { private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer; private final ShellExecutor mMainExecutor; private final SplitScreenImpl mImpl = new SplitScreenImpl(); + private final DisplayImeController mDisplayImeController; private StageCoordinator mStageCoordinator; public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, Context context, RootTaskDisplayAreaOrganizer rootTDAOrganizer, - ShellExecutor mainExecutor) { + ShellExecutor mainExecutor, DisplayImeController displayImeController) { mTaskOrganizer = shellTaskOrganizer; mSyncQueue = syncQueue; mContext = context; mRootTDAOrganizer = rootTDAOrganizer; mMainExecutor = mainExecutor; + mDisplayImeController = displayImeController; } public SplitScreen asSplitScreen() { @@ -84,7 +87,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter { if (mStageCoordinator == null) { // TODO: Multi-display mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, - mRootTDAOrganizer, mTaskOrganizer); + mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index e44c820a656a..22c97515ad76 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -39,6 +39,7 @@ import androidx.annotation.VisibleForTesting; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.split.SplitLayout; @@ -79,10 +80,12 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, private DisplayAreaInfo mDisplayAreaInfo; private final Context mContext; private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>(); + private final DisplayImeController mDisplayImeController; private boolean mExitSplitScreenOnHide = true; StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, - RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer) { + RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer, + DisplayImeController displayImeController) { mContext = context; mDisplayId = displayId; mSyncQueue = syncQueue; @@ -90,13 +93,14 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, mTaskOrganizer = taskOrganizer; mMainStage = new MainStage(mTaskOrganizer, mDisplayId, mMainStageListener, mSyncQueue); mSideStage = new SideStage(mTaskOrganizer, mDisplayId, mSideStageListener, mSyncQueue); + mDisplayImeController = displayImeController; mRootTDAOrganizer.registerListener(displayId, this); } @VisibleForTesting StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer, - MainStage mainStage, SideStage sideStage) { + MainStage mainStage, SideStage sideStage, DisplayImeController displayImeController) { mContext = context; mDisplayId = displayId; mSyncQueue = syncQueue; @@ -104,6 +108,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, mTaskOrganizer = taskOrganizer; mMainStage = mainStage; mSideStage = sideStage; + mDisplayImeController = displayImeController; mRootTDAOrganizer.registerListener(displayId, this); } @@ -420,7 +425,8 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, if (mSplitLayout == null) { mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext, mDisplayAreaInfo.configuration, this, - b -> mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b)); + b -> mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b), + mDisplayImeController); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java index 45d551528940..76497706ce4f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java @@ -43,6 +43,7 @@ import java.util.List; /** * Util class to create the view for a splash screen content. + * * @hide */ public class SplashscreenContentDrawer { @@ -349,7 +350,7 @@ public class SplashscreenContentDrawer { // Calculate the difference between two colors based on the HSV dimensions. final float normalizeH = minAngle / 180f; - final double square = Math.pow(normalizeH, 2) + final double square = Math.pow(normalizeH, 2) + Math.pow(aHsv[1] - bHsv[1], 2) + Math.pow(aHsv[2] - bHsv[2], 2); final double mean = square / 3; @@ -433,8 +434,11 @@ public class SplashscreenContentDrawer { */ private interface ColorTester { float nonTransparentRatio(); + boolean isComplexColor(); + int getDominantColor(); + boolean isGrayscale(); } @@ -511,14 +515,17 @@ public class SplashscreenContentDrawer { // restore to original bounds drawable.setBounds(initialBounds); - final Palette.Builder builder = new Palette.Builder(bitmap) - .maximumColorCount(5).clearFilters(); + final Palette.Builder builder; // The Palette API will ignore Alpha, so it cannot handle transparent pixels, but // sometimes we will need this information to know if this Drawable object is // transparent. mFilterTransparent = filterTransparent; if (mFilterTransparent) { - builder.setQuantizer(TRANSPARENT_FILTER_QUANTIZER); + builder = new Palette.Builder(bitmap, TRANSPARENT_FILTER_QUANTIZER) + .maximumColorCount(5); + } else { + builder = new Palette.Builder(bitmap, null) + .maximumColorCount(5); } mPalette = builder.generate(); bitmap.recycle(); @@ -538,7 +545,7 @@ public class SplashscreenContentDrawer { public int getDominantColor() { final Palette.Swatch mainSwatch = mPalette.getDominantSwatch(); if (mainSwatch != null) { - return mainSwatch.getRgb(); + return mainSwatch.getInt(); } return Color.BLACK; } @@ -549,7 +556,7 @@ public class SplashscreenContentDrawer { if (swatches != null) { for (int i = swatches.size() - 1; i >= 0; i--) { Palette.Swatch swatch = swatches.get(i); - if (!isGrayscaleColor(swatch.getRgb())) { + if (!isGrayscaleColor(swatch.getInt())) { return false; } } @@ -561,9 +568,9 @@ public class SplashscreenContentDrawer { private static final int NON_TRANSPARENT = 0xFF000000; private final Quantizer mInnerQuantizer = new VariationalKMeansQuantizer(); private float mNonTransparentRatio; + @Override - public void quantize(final int[] pixels, final int maxColors, - final Palette.Filter[] filters) { + public void quantize(final int[] pixels, final int maxColors) { mNonTransparentRatio = 0; int realSize = 0; for (int i = pixels.length - 1; i > 0; i--) { @@ -575,7 +582,7 @@ public class SplashscreenContentDrawer { if (DEBUG) { Slog.d(TAG, "quantize: this is pure transparent image"); } - mInnerQuantizer.quantize(pixels, maxColors, filters); + mInnerQuantizer.quantize(pixels, maxColors); return; } mNonTransparentRatio = (float) realSize / pixels.length; @@ -587,7 +594,7 @@ public class SplashscreenContentDrawer { rowIndex++; } } - mInnerQuantizer.quantize(samplePixels, maxColors, filters); + mInnerQuantizer.quantize(samplePixels, maxColors); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 59f8c1df1213..2182ee5590e1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -16,55 +16,78 @@ package com.android.wm.shell.transition; +import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; +import static android.window.TransitionInfo.FLAG_TRANSLUCENT; import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.Context; +import android.graphics.Rect; import android.os.IBinder; import android.util.ArrayMap; +import android.view.Choreographer; import android.view.SurfaceControl; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.Transformation; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; +import com.android.internal.R; +import com.android.internal.policy.AttributeCache; +import com.android.internal.policy.TransitionAnimation; +import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.protolog.ShellProtoLogGroup; import java.util.ArrayList; /** The default handler that handles anything not already handled. */ public class DefaultTransitionHandler implements Transitions.TransitionHandler { + private static final int MAX_ANIMATION_DURATION = 3000; + private final TransactionPool mTransactionPool; private final ShellExecutor mMainExecutor; private final ShellExecutor mAnimExecutor; + private final TransitionAnimation mTransitionAnimation; /** Keeps track of the currently-running animations associated with each transition. */ private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>(); - DefaultTransitionHandler(@NonNull TransactionPool transactionPool, + private float mTransitionAnimationScaleSetting = 1.0f; + + DefaultTransitionHandler(@NonNull TransactionPool transactionPool, Context context, @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) { mTransactionPool = transactionPool; mMainExecutor = mainExecutor; mAnimExecutor = animExecutor; + mTransitionAnimation = new TransitionAnimation(context, false /* debug */, Transitions.TAG); + + AttributeCache.init(context); } @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull Transitions.TransitionFinishCallback finishCallback) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, + "start default transition animation, info = %s", info); if (mAnimations.containsKey(transition)) { throw new IllegalStateException("Got a duplicate startAnimation call for " + transition); } final ArrayList<Animator> animations = new ArrayList<>(); mAnimations.put(transition, animations); - final boolean isOpening = Transitions.isOpeningType(info.getType()); final Runnable onAnimFinish = () -> { if (!animations.isEmpty()) return; @@ -77,19 +100,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { // Don't animate anything with an animating parent if (change.getParent() != null) continue; - final int mode = change.getMode(); - if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) { - if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { - // This received a transferred starting window, so don't animate - continue; - } - // fade in - startExampleAnimation( - animations, change.getLeash(), true /* show */, onAnimFinish); - } else if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) { - // fade out - startExampleAnimation( - animations, change.getLeash(), false /* show */, onAnimFinish); + Animation a = loadAnimation(info.getType(), change); + if (a != null) { + startAnimInternal(animations, a, change.getLeash(), onAnimFinish); } } t.apply(); @@ -105,32 +118,93 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { return null; } - // TODO(shell-transitions): real animations - private void startExampleAnimation(@NonNull ArrayList<Animator> animations, - @NonNull SurfaceControl leash, boolean show, @NonNull Runnable finishCallback) { - final float end = show ? 1.f : 0.f; - final float start = 1.f - end; + @Override + public void setAnimScaleSetting(float scale) { + mTransitionAnimationScaleSetting = scale; + } + + @Nullable + private Animation loadAnimation(int type, TransitionInfo.Change change) { + // TODO(b/178678389): It should handle more type animation here + Animation a = null; + + final boolean isOpening = Transitions.isOpeningType(type); + final int mode = change.getMode(); + final int flags = change.getFlags(); + + if (mode == TRANSIT_OPEN && isOpening) { + if ((flags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { + // This received a transferred starting window, so don't animate + return null; + } + + if (change.getTaskInfo() != null) { + a = mTransitionAnimation.loadDefaultAnimationAttr( + R.styleable.WindowAnimation_taskOpenEnterAnimation); + } else { + a = mTransitionAnimation.loadDefaultAnimationRes((flags & FLAG_TRANSLUCENT) == 0 + ? R.anim.activity_open_enter : R.anim.activity_translucent_open_enter); + } + } else if (mode == TRANSIT_TO_FRONT && isOpening) { + if ((flags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { + // This received a transferred starting window, so don't animate + return null; + } + + a = mTransitionAnimation.loadDefaultAnimationAttr( + R.styleable.WindowAnimation_taskToFrontEnterAnimation); + } else if (mode == TRANSIT_CLOSE && !isOpening) { + if (change.getTaskInfo() != null) { + a = mTransitionAnimation.loadDefaultAnimationAttr( + R.styleable.WindowAnimation_taskCloseExitAnimation); + } else { + a = mTransitionAnimation.loadDefaultAnimationRes((flags & FLAG_TRANSLUCENT) == 0 + ? R.anim.activity_close_exit : R.anim.activity_translucent_close_exit); + } + } else if (mode == TRANSIT_TO_BACK && !isOpening) { + a = mTransitionAnimation.loadDefaultAnimationAttr( + R.styleable.WindowAnimation_taskToBackExitAnimation); + } else if (mode == TRANSIT_CHANGE) { + // In the absence of a specific adapter, we just want to keep everything stationary. + a = new AlphaAnimation(1.f, 1.f); + a.setDuration(TransitionAnimation.DEFAULT_APP_TRANSITION_DURATION); + } + + if (a != null) { + Rect start = change.getStartAbsBounds(); + Rect end = change.getEndAbsBounds(); + a.restrictDuration(MAX_ANIMATION_DURATION); + a.initialize(end.width(), end.height(), start.width(), start.height()); + a.scaleCurrentDuration(mTransitionAnimationScaleSetting); + } + return a; + } + + private void startAnimInternal(@NonNull ArrayList<Animator> animations, @NonNull Animation anim, + @NonNull SurfaceControl leash, @NonNull Runnable finishCallback) { final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); - final ValueAnimator va = ValueAnimator.ofFloat(start, end); - va.setDuration(500); + final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f); + final Transformation transformation = new Transformation(); + final float[] matrix = new float[9]; + // Animation length is already expected to be scaled. + va.overrideDurationScale(1.0f); + va.setDuration(anim.computeDurationHint()); va.addUpdateListener(animation -> { - float fraction = animation.getAnimatedFraction(); - transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction); - transaction.apply(); + final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime()); + + applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix); }); + final Runnable finisher = () -> { - transaction.setAlpha(leash, end); - transaction.apply(); + applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix); + mTransactionPool.release(transaction); mMainExecutor.execute(() -> { animations.remove(va); finishCallback.run(); }); }; - va.addListener(new Animator.AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) { } - + va.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { finisher.run(); @@ -140,11 +214,17 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { public void onAnimationCancel(Animator animation) { finisher.run(); } - - @Override - public void onAnimationRepeat(Animator animation) { } }); animations.add(va); mAnimExecutor.execute(va::start); } + + private static void applyTransformation(long time, SurfaceControl.Transaction t, + SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix) { + anim.getTransformation(time, transformation); + t.setMatrix(leash, transformation.getMatrix(), matrix); + t.setAlpha(leash, transformation.getAlpha()); + t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId()); + t.apply(); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 10344892e766..89eee67bf5af 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -25,9 +25,13 @@ import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPI import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemProperties; +import android.provider.Settings; import android.util.ArrayMap; import android.util.Log; import android.view.SurfaceControl; @@ -43,6 +47,7 @@ import android.window.WindowOrganizer; import androidx.annotation.BinderThread; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; @@ -63,6 +68,7 @@ public class Transitions { SystemProperties.getBoolean("persist.debug.shell_transit", false); private final WindowOrganizer mOrganizer; + private final Context mContext; private final ShellExecutor mMainExecutor; private final ShellExecutor mAnimExecutor; private final TransitionPlayerImpl mPlayerImpl; @@ -72,6 +78,8 @@ public class Transitions { /** List of possible handlers. Ordered by specificity (eg. tapped back to front). */ private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>(); + private float mTransitionAnimationScaleSetting = 1.0f; + private static final class ActiveTransition { TransitionHandler mFirstHandler = null; } @@ -84,26 +92,46 @@ public class Transitions { } public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool, - @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) { + @NonNull Context context, @NonNull ShellExecutor mainExecutor, + @NonNull ShellExecutor animExecutor) { mOrganizer = organizer; + mContext = context; mMainExecutor = mainExecutor; mAnimExecutor = animExecutor; mPlayerImpl = new TransitionPlayerImpl(); // The very last handler (0 in the list) should be the default one. - mHandlers.add(new DefaultTransitionHandler(pool, mainExecutor, animExecutor)); + mHandlers.add(new DefaultTransitionHandler(pool, context, mainExecutor, animExecutor)); // Next lowest priority is remote transitions. mRemoteTransitionHandler = new RemoteTransitionHandler(mainExecutor); mHandlers.add(mRemoteTransitionHandler); + + ContentResolver resolver = context.getContentResolver(); + mTransitionAnimationScaleSetting = Settings.Global.getFloat(resolver, + Settings.Global.TRANSITION_ANIMATION_SCALE, + context.getResources().getFloat( + R.dimen.config_appTransitionAnimationDurationScaleDefault)); + dispatchAnimScaleSetting(mTransitionAnimationScaleSetting); + + resolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE), false, + new SettingsObserver()); } private Transitions() { mOrganizer = null; + mContext = null; mMainExecutor = null; mAnimExecutor = null; mPlayerImpl = null; mRemoteTransitionHandler = null; } + private void dispatchAnimScaleSetting(float scale) { + for (int i = mHandlers.size() - 1; i >= 0; --i) { + mHandlers.get(i).setAnimScaleSetting(scale); + } + } + /** Create an empty/non-registering transitions object for system-ui tests. */ @VisibleForTesting public static RemoteTransitions createEmptyForTesting() { @@ -368,6 +396,13 @@ public class Transitions { @Nullable WindowContainerTransaction handleRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request); + + /** + * Sets transition animation scale settings value to handler. + * + * @param scale The setting value of transition animation scale. + */ + default void setAnimScaleSetting(float scale) {} } @BinderThread @@ -404,4 +439,21 @@ public class Transitions { }); } } + + private class SettingsObserver extends ContentObserver { + + SettingsObserver() { + super(null); + } + + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + mTransitionAnimationScaleSetting = Settings.Global.getFloat( + mContext.getContentResolver(), Settings.Global.TRANSITION_ANIMATION_SCALE, + mTransitionAnimationScaleSetting); + + mMainExecutor.execute(() -> dispatchAnimScaleSetting(mTransitionAnimationScaleSetting)); + } + } } diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp index a57ac35583b2..9dd25fe0e6fe 100644 --- a/libs/WindowManager/Shell/tests/flicker/Android.bp +++ b/libs/WindowManager/Shell/tests/flicker/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test { name: "WMShellFlickerTests", srcs: ["src/**/*.java", "src/**/*.kt"], 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 3282ece999ac..10aea519f18b 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,239 +18,92 @@ package com.android.wm.shell.flicker import android.graphics.Region import android.view.Surface -import com.android.server.wm.flicker.dsl.LayersAssertionBuilder -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.FlickerTestParameter import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.traces.layers.getVisibleBounds -@JvmOverloads -fun LayersAssertionBuilder.appPairsDividerIsVisible(bugId: Int = 0) { - end("appPairsDividerIsVisible", bugId) { +fun FlickerTestParameter.appPairsDividerIsVisible() { + assertLayersEnd { this.isVisible(APP_PAIR_SPLIT_DIVIDER) } } -@JvmOverloads -fun LayersAssertionBuilder.appPairsDividerIsInvisible(bugId: Int = 0) { - end("appPairsDividerIsInVisible", bugId) { +fun FlickerTestParameter.appPairsDividerIsInvisible() { + assertLayersEnd { this.notExists(APP_PAIR_SPLIT_DIVIDER) } } -@JvmOverloads -fun LayersAssertionBuilder.appPairsDividerBecomesVisible(bugId: Int = 0) { - all("dividerLayerBecomesVisible", bugId) { +fun FlickerTestParameter.appPairsDividerBecomesVisible() { + assertLayers { this.hidesLayer(DOCKED_STACK_DIVIDER) .then() .showsLayer(DOCKED_STACK_DIVIDER) } } -@JvmOverloads -fun LayersAssertionBuilder.dockedStackDividerIsVisible(bugId: Int = 0) { - end("dockedStackDividerIsVisible", bugId) { +fun FlickerTestParameter.dockedStackDividerIsVisible() { + assertLayersEnd { this.isVisible(DOCKED_STACK_DIVIDER) } } -@JvmOverloads -fun LayersAssertionBuilder.dockedStackDividerBecomesVisible(bugId: Int = 0) { - all("dividerLayerBecomesVisible", bugId) { +fun FlickerTestParameter.dockedStackDividerBecomesVisible() { + assertLayers { this.hidesLayer(DOCKED_STACK_DIVIDER) .then() .showsLayer(DOCKED_STACK_DIVIDER) } } -@JvmOverloads -fun LayersAssertionBuilder.dockedStackDividerBecomesInvisible(bugId: Int = 0) { - all("dividerLayerBecomesInvisible", bugId) { +fun FlickerTestParameter.dockedStackDividerBecomesInvisible() { + assertLayers { this.showsLayer(DOCKED_STACK_DIVIDER) .then() .hidesLayer(DOCKED_STACK_DIVIDER) } } -@JvmOverloads -fun LayersAssertionBuilder.dockedStackDividerIsInvisible(bugId: Int = 0) { - end("dockedStackDividerIsInvisible", bugId) { +fun FlickerTestParameter.dockedStackDividerIsInvisible() { + assertLayersEnd { this.notExists(DOCKED_STACK_DIVIDER) } } -@JvmOverloads -fun LayersAssertionBuilder.appPairsPrimaryBoundsIsVisible( - rotation: Int, - primaryLayerName: String, - bugId: Int = 0 -) { - end("PrimaryAppBounds", bugId) { - val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER) - this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation)) - } -} - -@JvmOverloads -fun LayersAssertionBuilder.appPairsSecondaryBoundsIsVisible( - rotation: Int, - secondaryLayerName: String, - bugId: Int = 0 -) { - end("SecondaryAppBounds", bugId) { +fun FlickerTestParameter.appPairsPrimaryBoundsIsVisible(rotation: Int, primaryLayerName: String) { + assertLayersEnd { val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER) - this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation)) - } -} - -@JvmOverloads -fun LayersAssertionBuilder.dockedStackPrimaryBoundsIsVisible( - rotation: Int, - primaryLayerName: String, - bugId: Int = 0 -) { - end("PrimaryAppBounds", bugId) { - val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER) this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation)) } } -@JvmOverloads -fun LayersAssertionBuilder.dockedStackSecondaryBoundsIsVisible( +fun FlickerTestParameter.dockedStackPrimaryBoundsIsVisible( rotation: Int, - secondaryLayerName: String, - bugId: Int = 0 + primaryLayerName: String ) { - end("SecondaryAppBounds", bugId) { + assertLayersEnd { val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER) - this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation)) - } -} - -@JvmOverloads -fun LayersAssertionBuilderLegacy.appPairsDividerIsVisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - end("appPairsDividerIsVisible", bugId, enabled) { - this.isVisible(APP_PAIR_SPLIT_DIVIDER) - } -} - -@JvmOverloads -fun LayersAssertionBuilderLegacy.appPairsDividerIsInvisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - end("appPairsDividerIsInVisible", bugId, enabled) { - this.notExists(APP_PAIR_SPLIT_DIVIDER) - } -} - -@JvmOverloads -fun LayersAssertionBuilderLegacy.appPairsDividerBecomesVisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("dividerLayerBecomesVisible", bugId, enabled) { - this.hidesLayer(DOCKED_STACK_DIVIDER) - .then() - .showsLayer(DOCKED_STACK_DIVIDER) - } -} - -@JvmOverloads -fun LayersAssertionBuilderLegacy.dockedStackDividerIsVisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - end("dockedStackDividerIsVisible", bugId, enabled) { - this.isVisible(DOCKED_STACK_DIVIDER) - } -} - -@JvmOverloads -fun LayersAssertionBuilderLegacy.dockedStackDividerBecomesVisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("dividerLayerBecomesVisible", bugId, enabled) { - this.hidesLayer(DOCKED_STACK_DIVIDER) - .then() - .showsLayer(DOCKED_STACK_DIVIDER) - } -} - -@JvmOverloads -fun LayersAssertionBuilderLegacy.dockedStackDividerBecomesInvisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("dividerLayerBecomesInvisible", bugId, enabled) { - this.showsLayer(DOCKED_STACK_DIVIDER) - .then() - .hidesLayer(DOCKED_STACK_DIVIDER) - } -} - -@JvmOverloads -fun LayersAssertionBuilderLegacy.dockedStackDividerIsInvisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - end("dockedStackDividerIsInvisible", bugId, enabled) { - this.notExists(DOCKED_STACK_DIVIDER) - } -} - -@JvmOverloads -fun LayersAssertionBuilderLegacy.appPairsPrimaryBoundsIsVisible( - rotation: Int, - primaryLayerName: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - end("PrimaryAppBounds", bugId, enabled) { - val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER) this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation)) } } -@JvmOverloads -fun LayersAssertionBuilderLegacy.appPairsSecondaryBoundsIsVisible( +fun FlickerTestParameter.appPairsSecondaryBoundsIsVisible( rotation: Int, - secondaryLayerName: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 + secondaryLayerName: String ) { - end("SecondaryAppBounds", bugId, enabled) { + assertLayersEnd { val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER) this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation)) } } -@JvmOverloads -fun LayersAssertionBuilderLegacy.dockedStackPrimaryBoundsIsVisible( +fun FlickerTestParameter.dockedStackSecondaryBoundsIsVisible( rotation: Int, - primaryLayerName: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 + secondaryLayerName: String ) { - end("PrimaryAppBounds", bugId, enabled) { - val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER) - this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation)) - } -} - -@JvmOverloads -fun LayersAssertionBuilderLegacy.dockedStackSecondaryBoundsIsVisible( - rotation: Int, - secondaryLayerName: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - end("SecondaryAppBounds", bugId, enabled) { + assertLayersEnd { val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER) this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation)) } @@ -260,10 +113,10 @@ fun getPrimaryRegion(dividerRegion: Region, rotation: Int): Region { val displayBounds = WindowUtils.getDisplayBounds(rotation) return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) { Region(0, 0, displayBounds.bounds.right, - dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset) + dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset) } else { Region(0, 0, dividerRegion.bounds.left, - dividerRegion.bounds.right - WindowUtils.dockedStackDividerInset) + dividerRegion.bounds.right - WindowUtils.dockedStackDividerInset) } } @@ -271,12 +124,12 @@ fun getSecondaryRegion(dividerRegion: Region, rotation: Int): Region { val displayBounds = WindowUtils.getDisplayBounds(rotation) return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) { Region(0, - dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset, - displayBounds.bounds.right, - displayBounds.bounds.bottom - WindowUtils.dockedStackDividerInset) + dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset, + displayBounds.bounds.right, + displayBounds.bounds.bottom - WindowUtils.dockedStackDividerInset) } else { Region(dividerRegion.bounds.right, 0, - displayBounds.bounds.right, - displayBounds.bounds.bottom - WindowUtils.dockedStackDividerInset) + displayBounds.bounds.right, + displayBounds.bounds.bottom - WindowUtils.dockedStackDividerInset) } -} +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt index 89bbdb0a2f99..9c50630095be 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt @@ -16,33 +16,33 @@ package com.android.wm.shell.flicker +import android.app.Instrumentation import android.content.pm.PackageManager import android.content.pm.PackageManager.FEATURE_LEANBACK import android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY -import android.os.RemoteException -import android.os.SystemClock -import android.platform.helpers.IAppHelper import android.view.Surface import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice -import com.android.server.wm.flicker.Flicker import org.junit.Assume.assumeFalse import org.junit.Before +import org.junit.runners.Parameterized /** * Base class of all Flicker test that performs common functions for all flicker tests: * - * * - Caches transitions so that a transition is run once and the transition results are used by * tests multiple times. This is needed for parameterized tests which call the BeforeClass methods * multiple times. * - Keeps track of all test artifacts and deletes ones which do not need to be reviewed. * - Fails tests if results are not available for any test due to jank. */ -abstract class FlickerTestBase { - val instrumentation by lazy { InstrumentationRegistry.getInstrumentation() } - val uiDevice by lazy { UiDevice.getInstance(instrumentation) } - val packageManager: PackageManager by lazy { instrumentation.context.getPackageManager() } +abstract class FlickerTestBase( + protected val rotationName: String, + protected val rotation: Int +) { + val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + val uiDevice = UiDevice.getInstance(instrumentation) + val packageManager: PackageManager = instrumentation.context.packageManager protected val isTelevision: Boolean by lazy { packageManager.run { hasSystemFeature(FEATURE_LEANBACK) || hasSystemFeature(FEATURE_LEANBACK_ONLY) @@ -56,83 +56,12 @@ abstract class FlickerTestBase { @Before open fun televisionSetUp() = assumeFalse(isTelevision) - /** - * Build a test tag for the test - * @param testName Name of the transition(s) being tested - * @param app App being launcher - * @param rotation Initial screen rotation - * - * @return test tag with pattern <NAME>__<APP>__<ROTATION> - </ROTATION></APP></NAME> */ - protected fun buildTestTag(testName: String, app: IAppHelper, rotation: Int): String { - return buildTestTag( - testName, app, rotation, rotation, app2 = null, extraInfo = "") - } - - /** - * Build a test tag for the test - * @param testName Name of the transition(s) being tested - * @param app App being launcher - * @param beginRotation Initial screen rotation - * @param endRotation End screen rotation (if any, otherwise use same as initial) - * - * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION> - </END_ROTATION></BEGIN_ROTATION></APP></NAME> */ - protected fun buildTestTag( - testName: String, - app: IAppHelper, - beginRotation: Int, - endRotation: Int - ): String { - return buildTestTag( - testName, app, beginRotation, endRotation, app2 = null, extraInfo = "") - } - - /** - * Build a test tag for the test - * @param testName Name of the transition(s) being tested - * @param app App being launcher - * @param app2 Second app being launched (if any) - * @param beginRotation Initial screen rotation - * @param endRotation End screen rotation (if any, otherwise use same as initial) - * @param extraInfo Additional information to append to the tag - * - * @return test tag with pattern <NAME>__<APP></APP>(S)>__<ROTATION></ROTATION>(S)>[__<EXTRA>] - </EXTRA></NAME> */ - protected fun buildTestTag( - testName: String, - app: IAppHelper, - beginRotation: Int, - endRotation: Int, - app2: IAppHelper?, - extraInfo: String - ): String { - var testTag = "${testName}__${app.launcherName}" - if (app2 != null) { - testTag += "-${app2.launcherName}" - } - testTag += "__${Surface.rotationToString(beginRotation)}" - if (endRotation != beginRotation) { - testTag += "-${Surface.rotationToString(endRotation)}" - } - if (extraInfo.isNotEmpty()) { - testTag += "__$extraInfo" - } - return testTag - } - - protected fun Flicker.setRotation(rotation: Int) { - try { - when (rotation) { - Surface.ROTATION_270 -> device.setOrientationLeft() - Surface.ROTATION_90 -> device.setOrientationRight() - Surface.ROTATION_0 -> device.setOrientationNatural() - else -> device.setOrientationNatural() - } - // Wait for animation to complete - SystemClock.sleep(1000) - } catch (e: RemoteException) { - throw RuntimeException(e) + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90) + return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NonRotationTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NonRotationTestBase.kt deleted file mode 100644 index 90334ae91e9d..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NonRotationTestBase.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.flicker - -import android.view.Surface -import org.junit.runners.Parameterized - -abstract class NonRotationTestBase( - protected val rotationName: String, - protected val rotation: Int -) : FlickerTestBase() { - companion object { - const val SCREENSHOT_LAYER = "RotationLayer" - - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90) - return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt index c3fd66395366..5d51b2fd515f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt @@ -18,15 +18,16 @@ package com.android.wm.shell.flicker.apppairs import android.os.Bundle import android.os.SystemClock +import android.platform.test.annotations.Presubmit import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.buildTestTag -import com.android.wm.shell.flicker.helpers.AppPairsHelper import com.android.wm.shell.flicker.appPairsDividerIsInvisible +import com.android.wm.shell.flicker.helpers.AppPairsHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -41,47 +42,47 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class AppPairsTestCannotPairNonResizeableApps( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : AppPairsTransition(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): List<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag(configuration) - } - transitions { - nonResizeableApp?.launchViaIntent(wmHelper) - // TODO pair apps through normal UX flow - executeShellCommand( - composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true)) - SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) - } - assertions { - presubmit { - layersTrace { - appPairsDividerIsInvisible() - } - windowManagerTrace { - val nonResizeableApp = nonResizeableApp - require(nonResizeableApp != null) { - "Non resizeable app not initialized" - } + testSpec: FlickerTestParameter +) : AppPairsTransition(testSpec) { - end("onlyResizeableAppWindowVisible") { - isVisible(nonResizeableApp.defaultWindowName) - isInvisible(primaryApp.defaultWindowName) - } - } - } - } + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { + super.transition(this, it) + transitions { + nonResizeableApp?.launchViaIntent(wmHelper) + // TODO pair apps through normal UX flow + executeShellCommand( + composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true)) + SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) } + } + + @Presubmit + @Test + fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible() - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - transition, testSpec, repetitions = AppPairsHelper.TEST_REPETITIONS) + @Presubmit + @Test + fun onlyResizeableAppWindowVisible() { + val nonResizeableApp = nonResizeableApp + require(nonResizeableApp != null) { + "Non resizeable app not initialized" + } + testSpec.assertWmEnd { + isVisible(nonResizeableApp.defaultWindowName) + isInvisible(primaryApp.defaultWindowName) + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( + repetitions = AppPairsHelper.TEST_REPETITIONS) } } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt index 7a2a5e482d98..77890ba8ed15 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt @@ -18,17 +18,19 @@ package com.android.wm.shell.flicker.apppairs import android.os.Bundle import android.os.SystemClock +import android.platform.test.annotations.Presubmit +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.traces.layers.getVisibleBounds import com.android.wm.shell.flicker.appPairsDividerIsVisible import com.android.wm.shell.flicker.helpers.AppPairsHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -39,52 +41,53 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class AppPairsTestPairPrimaryAndSecondaryApps( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : AppPairsTransition(InstrumentationRegistry.getInstrumentation()) { + testSpec: FlickerTestParameter +) : AppPairsTransition(testSpec) { + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { + super.transition(this, it) + transitions { + // TODO pair apps through normal UX flow + executeShellCommand( + composePairsCommand(primaryTaskId, secondaryTaskId, pair = true)) + SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) + } + } + + @Presubmit + @Test + fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible() + + @Presubmit + @Test + fun bothAppWindowsVisible() { + testSpec.assertWmEnd { + isVisible(primaryApp.defaultWindowName) + isVisible(secondaryApp.defaultWindowName) + } + } + + @FlakyTest + @Test + fun appsEndingBounds() { + testSpec.assertLayersEnd { + val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER) + this.hasVisibleRegion(primaryApp.defaultWindowName, + appPairsHelper.getPrimaryBounds(dividerRegion)) + .hasVisibleRegion(secondaryApp.defaultWindowName, + appPairsHelper.getSecondaryBounds(dividerRegion)) + } + } + + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag(configuration) - } - transitions { - // TODO pair apps through normal UX flow - executeShellCommand( - composePairsCommand(primaryTaskId, secondaryTaskId, pair = true)) - SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) - } - assertions { - presubmit { - layersTrace { - appPairsDividerIsVisible() - } - windowManagerTrace { - end("bothAppWindowsVisible") { - isVisible(primaryApp.defaultWindowName) - isVisible(secondaryApp.defaultWindowName) - } - } - } - - flaky { - layersTrace { - end("appsEndingBounds") { - val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER) - this.hasVisibleRegion(primaryApp.defaultWindowName, - appPairsHelper.getPrimaryBounds(dividerRegion)) - .hasVisibleRegion(secondaryApp.defaultWindowName, - appPairsHelper.getSecondaryBounds(dividerRegion)) - } - } - } - } - } - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, transition, - testSpec, repetitions = AppPairsHelper.TEST_REPETITIONS) + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( + repetitions = AppPairsHelper.TEST_REPETITIONS) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt index d8dc4c2b56f6..3d3ca0cfd450 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt @@ -18,17 +18,19 @@ package com.android.wm.shell.flicker.apppairs import android.os.Bundle import android.os.SystemClock +import android.platform.test.annotations.Presubmit +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.traces.layers.getVisibleBounds import com.android.wm.shell.flicker.appPairsDividerIsInvisible import com.android.wm.shell.flicker.helpers.AppPairsHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -39,61 +41,67 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class AppPairsTestUnpairPrimaryAndSecondaryApps( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : AppPairsTransition(InstrumentationRegistry.getInstrumentation()) { + testSpec: FlickerTestParameter +) : AppPairsTransition(testSpec) { + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { + super.transition(this, it) + setup { + executeShellCommand( + composePairsCommand(primaryTaskId, secondaryTaskId, pair = true)) + SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) + } + transitions { + // TODO pair apps through normal UX flow + executeShellCommand( + composePairsCommand(primaryTaskId, secondaryTaskId, pair = false)) + SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) + } + } + + @Presubmit + @Test + fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible() + + @Presubmit + @Test + fun bothAppWindowsInvisible() { + testSpec.assertWmEnd { + isInvisible(primaryApp.defaultWindowName) + isInvisible(secondaryApp.defaultWindowName) + } + } + + @FlakyTest + @Test + fun appsStartingBounds() { + testSpec.assertLayersStart { + val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER) + hasVisibleRegion(primaryApp.defaultWindowName, + appPairsHelper.getPrimaryBounds(dividerRegion)) + hasVisibleRegion(secondaryApp.defaultWindowName, + appPairsHelper.getSecondaryBounds(dividerRegion)) + } + } + + @FlakyTest + @Test + fun appsEndingBounds() { + testSpec.assertLayersEnd { + notExists(primaryApp.defaultWindowName) + notExists(secondaryApp.defaultWindowName) + } + } + + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag(configuration) - } - setup { - executeShellCommand( - composePairsCommand(primaryTaskId, secondaryTaskId, pair = true)) - SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) - } - transitions { - // TODO pair apps through normal UX flow - executeShellCommand( - composePairsCommand(primaryTaskId, secondaryTaskId, pair = false)) - SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) - } - assertions { - presubmit { - layersTrace { - appPairsDividerIsInvisible() - } - windowManagerTrace { - end("bothAppWindowsInvisible") { - isInvisible(primaryApp.defaultWindowName) - isInvisible(secondaryApp.defaultWindowName) - } - } - } - - flaky { - layersTrace { - start("appsStartingBounds") { - val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER) - this.hasVisibleRegion(primaryApp.defaultWindowName, - appPairsHelper.getPrimaryBounds(dividerRegion)) - .hasVisibleRegion(secondaryApp.defaultWindowName, - appPairsHelper.getSecondaryBounds(dividerRegion)) - } - end("appsEndingBounds") { - this.notExists(primaryApp.defaultWindowName) - .notExists(secondaryApp.defaultWindowName) - } - } - } - } - } - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, transition, - testSpec, repetitions = AppPairsHelper.TEST_REPETITIONS) + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( + repetitions = AppPairsHelper.TEST_REPETITIONS) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt index 78a938aef69e..9e6752db224f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt @@ -18,38 +18,53 @@ package com.android.wm.shell.flicker.apppairs import android.app.Instrumentation import android.os.Bundle +import android.platform.test.annotations.Presubmit import android.system.helpers.ActivityHelper import android.util.Log +import androidx.test.platform.app.InstrumentationRegistry import com.android.compatibility.common.util.SystemUtil +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.helpers.isRotated import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.helpers.AppPairsHelper import com.android.wm.shell.flicker.helpers.SplitScreenHelper import com.android.wm.shell.flicker.testapp.Components +import org.junit.Test import java.io.IOException -open class AppPairsTransition( - protected val instrumentation: Instrumentation -) { - internal val activityHelper = ActivityHelper.getInstance() - - internal val appPairsHelper = AppPairsHelper(instrumentation, +abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter) { + protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + protected val isRotated = testSpec.config.startRotation.isRotated() + protected val activityHelper = ActivityHelper.getInstance() + protected val appPairsHelper = AppPairsHelper(instrumentation, Components.SplitScreenActivity.LABEL, Components.SplitScreenActivity.COMPONENT) - internal val primaryApp = SplitScreenHelper.getPrimary(instrumentation) - internal val secondaryApp = SplitScreenHelper.getSecondary(instrumentation) - internal open val nonResizeableApp: SplitScreenHelper? = + protected val primaryApp = SplitScreenHelper.getPrimary(instrumentation) + protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation) + protected open val nonResizeableApp: SplitScreenHelper? = SplitScreenHelper.getNonResizeable(instrumentation) - internal var primaryTaskId = "" - internal var secondaryTaskId = "" - internal var nonResizeableTaskId = "" + protected var primaryTaskId = "" + protected var secondaryTaskId = "" + protected var nonResizeableTaskId = "" + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + transition(this, testSpec.config) + } + } internal open val transition: FlickerBuilder.(Bundle) -> Unit get() = { configuration -> @@ -71,20 +86,9 @@ open class AppPairsTransition( primaryTaskId, secondaryTaskId, pair = false)) executeShellCommand(composePairsCommand( primaryTaskId, nonResizeableTaskId, pair = false)) - primaryApp.exit() - secondaryApp.exit() - nonResizeableApp?.exit() - } - } - - assertions { - layersTrace { - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - } - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() + primaryApp.exit(wmHelper) + secondaryApp.exit(wmHelper) + nonResizeableApp?.exit(wmHelper) } } } @@ -128,4 +132,20 @@ open class AppPairsTransition( } append("$primaryApp $secondaryApp") } + + @Presubmit + @Test + open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Presubmit + @Test + open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() }
\ No newline at end of file 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 8aee005b7513..35a0020b16a9 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 @@ -18,25 +18,25 @@ package com.android.wm.shell.flicker.apppairs import android.os.Bundle import android.os.SystemClock +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation -import com.android.server.wm.flicker.helpers.buildTestTag 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.statusBarLayerRotatesScales -import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.appPairsDividerIsVisible import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible import com.android.wm.shell.flicker.helpers.AppPairsHelper import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -47,57 +47,65 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class RotateTwoLaunchedAppsInAppPairsMode( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : RotateTwoLaunchedAppsTransition( - InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag(configuration) - } - transitions { - executeShellCommand(composePairsCommand( - primaryTaskId, secondaryTaskId, true /* pair */)) - SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) - setRotation(configuration.endRotation) - } - assertions { - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - end("bothAppWindowsVisible") { - isVisible(primaryApp.defaultWindowName) - .isVisible(secondaryApp.defaultWindowName) - } - } - } - - flaky { - layersTrace { - appPairsDividerIsVisible() - navBarLayerRotatesAndScales(Surface.ROTATION_0, - configuration.endRotation) - statusBarLayerRotatesScales(Surface.ROTATION_0, - configuration.endRotation) - appPairsPrimaryBoundsIsVisible(configuration.endRotation, - primaryApp.defaultWindowName, bugId = 172776659) - appPairsSecondaryBoundsIsVisible(configuration.endRotation, - secondaryApp.defaultWindowName, bugId = 172776659) - } - } - } + testSpec: FlickerTestParameter +) : RotateTwoLaunchedAppsTransition(testSpec) { + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { + super.transition(this, it) + transitions { + executeShellCommand(composePairsCommand( + primaryTaskId, secondaryTaskId, true /* pair */)) + SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) + setRotation(testSpec.config.endRotation) } + } + + @Presubmit + @Test + fun bothAppWindowsVisible() { + testSpec.assertWmEnd { + isVisible(primaryApp.defaultWindowName) + .isVisible(secondaryApp.defaultWindowName) + } + } - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - transition, testSpec, + @FlakyTest + @Test + fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible() + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, + testSpec.config.endRotation) + + @FlakyTest + @Test + fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, + testSpec.config.endRotation) + + @FlakyTest(bugId = 172776659) + @Test + fun appPairsPrimaryBoundsIsVisible() = + testSpec.appPairsPrimaryBoundsIsVisible(testSpec.config.endRotation, + primaryApp.defaultWindowName) + + @FlakyTest(bugId = 172776659) + @Test + fun appPairsSecondaryBoundsIsVisible() = + testSpec.appPairsSecondaryBoundsIsVisible(testSpec.config.endRotation, + secondaryApp.defaultWindowName) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_90, Surface.ROTATION_270)) + supportedRotations = listOf(Surface.ROTATION_90, Surface.ROTATION_270) + ) } } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt index bc99c9430f13..326a775acc8d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt @@ -20,18 +20,16 @@ import android.os.Bundle import android.os.SystemClock import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.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 @@ -39,7 +37,9 @@ import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible import com.android.wm.shell.flicker.helpers.AppPairsHelper import com.android.wm.shell.flicker.helpers.SplitScreenHelper +import org.junit.Assume import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -48,70 +48,84 @@ import org.junit.runners.Parameterized * Test open apps to app pairs and rotate. * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppsRotateAndEnterAppPairsMode` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : RotateTwoLaunchedAppsTransition( - InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag(configuration) - } - transitions { - this.setRotation(configuration.endRotation) - executeShellCommand( - composePairsCommand(primaryTaskId, secondaryTaskId, pair = true)) - SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) - } - assertions { - val isRotated = configuration.startRotation.isRotated() - presubmit { - layersTrace { - statusBarLayerRotatesScales(Surface.ROTATION_0, - configuration.endRotation) - appPairsDividerIsVisible() - if (!isRotated) { - navBarLayerRotatesAndScales(Surface.ROTATION_0, - configuration.endRotation) - } - } - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - end("bothAppWindowsVisible") { - isVisible(primaryApp.defaultWindowName) - isVisible(secondaryApp.defaultWindowName) - } - } - } - flaky { - layersTrace { - appPairsPrimaryBoundsIsVisible(configuration.endRotation, - primaryApp.defaultWindowName, 172776659) - appPairsSecondaryBoundsIsVisible(configuration.endRotation, - secondaryApp.defaultWindowName, 172776659) - - if (isRotated) { - navBarLayerRotatesAndScales(Surface.ROTATION_0, - configuration.endRotation) - } - } - } - } + testSpec: FlickerTestParameter +) : RotateTwoLaunchedAppsTransition(testSpec) { + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { + super.transition(this, it) + transitions { + this.setRotation(testSpec.config.endRotation) + executeShellCommand( + composePairsCommand(primaryTaskId, secondaryTaskId, pair = true)) + SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) } + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, + testSpec.config.endRotation) + + @Presubmit + @Test + fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible() + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(isRotated) + testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation) + } + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales_Flaky() { + Assume.assumeTrue(isRotated) + testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation) + } - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - transition, testSpec, + @Presubmit + @Test + override fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + override fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun bothAppWindowsVisible() { + testSpec.assertWmEnd { + isVisible(primaryApp.defaultWindowName) + isVisible(secondaryApp.defaultWindowName) + } + } + + @FlakyTest(bugId = 172776659) + @Test + fun appPairsPrimaryBoundsIsVisible() = + testSpec.appPairsPrimaryBoundsIsVisible(testSpec.config.endRotation, + primaryApp.defaultWindowName) + + @FlakyTest(bugId = 172776659) + @Test + fun appPairsSecondaryBoundsIsVisible() = + testSpec.appPairsSecondaryBoundsIsVisible(testSpec.config.endRotation, + secondaryApp.defaultWindowName) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, supportedRotations = listOf(Surface.ROTATION_90, Surface.ROTATION_270) ) } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt index 8ea2544fcf61..271b25fc0ce1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt @@ -16,17 +16,17 @@ package com.android.wm.shell.flicker.apppairs -import android.app.Instrumentation import android.os.Bundle import android.view.Surface +import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.wm.shell.flicker.helpers.SplitScreenHelper -open class RotateTwoLaunchedAppsTransition( - instrumentation: Instrumentation -) : AppPairsTransition(instrumentation) { +abstract class RotateTwoLaunchedAppsTransition( + testSpec: FlickerTestParameter +) : AppPairsTransition(testSpec) { override val nonResizeableApp: SplitScreenHelper? get() = null @@ -45,8 +45,8 @@ open class RotateTwoLaunchedAppsTransition( eachRun { executeShellCommand(composePairsCommand( primaryTaskId, secondaryTaskId, pair = false)) - primaryApp.exit() - secondaryApp.exit() + primaryApp.exit(wmHelper) + secondaryApp.exit(wmHelper) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt index 0d9edd29d259..9b70fac737e6 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt @@ -19,13 +19,13 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.os.Bundle import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.WALLPAPER_TITLE import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.startRotation @@ -36,6 +36,7 @@ import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -47,56 +48,74 @@ import org.junit.runners.Parameterized @Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) // @FlakyTest(bugId = 179116910) class EnterSplitScreenDockActivity( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { + testSpec: FlickerTestParameter +) : LegacySplitScreenTransition(testSpec) { + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { configuration -> + super.transition(this, configuration) + transitions { + device.launchSplitScreen(wmHelper) + } + } + + @FlakyTest(bugId = 169271943) + @Test + fun dockedStackPrimaryBoundsIsVisible() = + testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation, + splitScreenApp.defaultWindowName) + + @Presubmit + @Test + fun dockedStackDividerBecomesVisible() = testSpec.dockedStackDividerBecomesVisible() + + @FlakyTest(bugId = 178531736) + @Test + // b/178531736 + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, + WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME, + splitScreenApp.defaultWindowName) + ) + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @FlakyTest(bugId = 178531736) + @Test + // b/178531736 + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, + WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME, + splitScreenApp.defaultWindowName) + ) + + @Presubmit + @Test + fun appWindowIsVisible() { + testSpec.assertWmEnd { + isVisible(splitScreenApp.defaultWindowName) + } + } + + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testLegacySplitScreenDockActivity", configuration) - } - repeat { SplitScreenHelper.TEST_REPETITIONS } - transitions { - device.launchSplitScreen(wmHelper) - } - assertions { - layersTrace { - dockedStackPrimaryBoundsIsVisible( - configuration.startRotation, - splitScreenApp.defaultWindowName, bugId = 169271943) - dockedStackDividerBecomesVisible() - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, - WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME, - splitScreenApp.defaultWindowName), - bugId = 178531736 - ) - } - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, - WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME, - splitScreenApp.defaultWindowName), - bugId = 178531736 - ) - end("appWindowIsVisible") { - isVisible(splitScreenApp.defaultWindowName) - } - } - } - } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, defaultTransitionSetup, testSpec, + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910 ) } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt index a513ee1e91f1..bd57a59ea3d9 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt @@ -19,13 +19,13 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.os.Bundle import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.appWindowBecomesVisible import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.helpers.reopenAppFromOverview import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible @@ -38,6 +38,7 @@ import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -46,61 +47,79 @@ import org.junit.runners.Parameterized * Test open activity to primary split screen and dock secondary activity to side * To run this test: `atest WMShellFlickerTests:EnterSplitScreenLaunchToSide` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class EnterSplitScreenLaunchToSide( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { + testSpec: FlickerTestParameter +) : LegacySplitScreenTransition(testSpec) { + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { configuration -> + super.transition(this, configuration) + transitions { + device.launchSplitScreen(wmHelper) + device.reopenAppFromOverview(wmHelper) + } + } + + @FlakyTest(bugId = 169271943) + @Test + fun dockedStackPrimaryBoundsIsVisible() = + testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation, + splitScreenApp.defaultWindowName) + + @FlakyTest(bugId = 169271943) + @Test + fun dockedStackSecondaryBoundsIsVisible() = + testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation, + secondaryApp.defaultWindowName) + + @Presubmit + @Test + // b/169271943 + fun dockedStackDividerBecomesVisible() = testSpec.dockedStackDividerBecomesVisible() + + @FlakyTest(bugId = 178447631) + @Test + // TODO(b/178447631) Remove Splash Screen from white list when flicker lib + // add a wait for splash screen be gone + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, SPLASH_SCREEN_NAME, + splitScreenApp.defaultWindowName, + secondaryApp.defaultWindowName) + ) + + @Presubmit + @Test + fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp.defaultWindowName) + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, SPLASH_SCREEN_NAME, + splitScreenApp.defaultWindowName, + secondaryApp.defaultWindowName) + ) + + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testLegacySplitScreenLaunchToSide", configuration) - } - repeat { SplitScreenHelper.TEST_REPETITIONS } - transitions { - device.launchSplitScreen(wmHelper) - device.reopenAppFromOverview(wmHelper) - } - assertions { - layersTrace { - dockedStackPrimaryBoundsIsVisible( - configuration.startRotation, - splitScreenApp.defaultWindowName, bugId = 169271943) - dockedStackSecondaryBoundsIsVisible( - configuration.startRotation, - secondaryApp.defaultWindowName, bugId = 169271943) - dockedStackDividerBecomesVisible() - // TODO(b/178447631) Remove Splash Screen from white list when flicker lib - // add a wait for splash screen be gone - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, SPLASH_SCREEN_NAME, - splitScreenApp.defaultWindowName, - secondaryApp.defaultWindowName), - bugId = 178447631 - ) - } - windowManagerTrace { - appWindowBecomesVisible(secondaryApp.defaultWindowName) - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, SPLASH_SCREEN_NAME, - splitScreenApp.defaultWindowName, - secondaryApp.defaultWindowName) - ) - } - } - } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, defaultTransitionSetup, testSpec, + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, supportedRotations = listOf(Surface.ROTATION_0) // bugId = 175687842 ) } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt index 78ed773f2409..67578b29a36c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt @@ -17,16 +17,14 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.os.Bundle -import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.WALLPAPER_TITLE import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.canSplitScreen import com.android.server.wm.flicker.helpers.openQuickstep import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry @@ -35,6 +33,7 @@ import com.android.wm.shell.flicker.dockedStackDividerIsInvisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.Assert import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -43,64 +42,68 @@ import org.junit.runners.Parameterized * Test open non-resizable activity will auto exit split screen mode * To run this test: `atest WMShellFlickerTests:EnterSplitScreenNonResizableNotDock` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FlakyTest(bugId = 173875043) class EnterSplitScreenNonResizableNotDock( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testLegacySplitScreenNonResizeableActivityNotDock", configuration) - } - repeat { SplitScreenHelper.TEST_REPETITIONS } - teardown { - eachRun { - nonResizeableApp.exit(wmHelper) - } - } - transitions { - nonResizeableApp.launchViaIntent(wmHelper) - device.openQuickstep(wmHelper) - if (device.canSplitScreen(wmHelper)) { - Assert.fail("Non-resizeable app should not enter split screen") - } + testSpec: FlickerTestParameter +) : LegacySplitScreenTransition(testSpec) { + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { configuration -> + super.transition(this, configuration) + teardown { + eachRun { + nonResizeableApp.exit(wmHelper) } - assertions { - layersTrace { - dockedStackDividerIsInvisible() - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, - SPLASH_SCREEN_NAME, - nonResizeableApp.defaultWindowName, - splitScreenApp.defaultWindowName), - bugId = 178447631 - ) - } - windowManagerTrace { - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(WALLPAPER_TITLE, - LAUNCHER_PACKAGE_NAME, - SPLASH_SCREEN_NAME, - nonResizeableApp.defaultWindowName, - splitScreenApp.defaultWindowName) - ) - end("appWindowIsVisible") { - isInvisible(nonResizeableApp.defaultWindowName) - } - } + } + transitions { + nonResizeableApp.launchViaIntent(wmHelper) + device.openQuickstep(wmHelper) + if (device.canSplitScreen(wmHelper)) { + Assert.fail("Non-resizeable app should not enter split screen") } } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, cleanSetup, testSpec, + } + + @Test + fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible() + + @FlakyTest(bugId = 178447631) + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, + SPLASH_SCREEN_NAME, + nonResizeableApp.defaultWindowName, + splitScreenApp.defaultWindowName) + ) + + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( + listOf(WALLPAPER_TITLE, + LAUNCHER_PACKAGE_NAME, + SPLASH_SCREEN_NAME, + nonResizeableApp.defaultWindowName, + splitScreenApp.defaultWindowName) + ) + + @Test + fun appWindowIsVisible() { + testSpec.assertWmEnd { + isInvisible(nonResizeableApp.defaultWindowName) + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */)) + supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 } } }
\ No newline at end of file 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 f4e5ba7877da..5d42a4a8fae0 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 @@ -19,14 +19,14 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.os.Bundle import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.appWindowBecomesInVisible import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.exitSplitScreenFromBottom import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.layerBecomesInvisible @@ -36,6 +36,7 @@ import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEnt import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -44,61 +45,72 @@ import org.junit.runners.Parameterized * Test open resizeable activity split in primary, and drag divider to bottom exit split screen * To run this test: `atest WMShellFlickerTests:ExitLegacySplitScreenFromBottom` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class ExitLegacySplitScreenFromBottom( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testExitLegacySplitScreenFromBottom", configuration) - } - repeat { SplitScreenHelper.TEST_REPETITIONS } - setup { - eachRun { - splitScreenApp.launchViaIntent(wmHelper) - device.launchSplitScreen(wmHelper) - } - } - teardown { - eachRun { - splitScreenApp.exit(wmHelper) - } - } - transitions { - device.exitSplitScreenFromBottom() + testSpec: FlickerTestParameter +) : LegacySplitScreenTransition(testSpec) { + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { configuration -> + super.transition(this, configuration) + setup { + eachRun { + splitScreenApp.launchViaIntent(wmHelper) + device.launchSplitScreen(wmHelper) } - assertions { - layersTrace { - layerBecomesInvisible(DOCKED_STACK_DIVIDER) - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, - secondaryApp.defaultWindowName), - bugId = 178447631 - ) - } - windowManagerTrace { - appWindowBecomesInVisible(secondaryApp.defaultWindowName) - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, - secondaryApp.defaultWindowName), - bugId = 178447631 - ) - } + } + teardown { + eachRun { + splitScreenApp.exit(wmHelper) } } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, defaultTransitionSetup, testSpec, + transitions { + device.exitSplitScreenFromBottom() + } + } + + @Presubmit + @Test + fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(DOCKED_STACK_DIVIDER) + + @FlakyTest(bugId = 178447631) + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, + secondaryApp.defaultWindowName) + ) + + @Presubmit + @Test + fun appWindowBecomesInVisible() = + testSpec.appWindowBecomesInVisible(secondaryApp.defaultWindowName) + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @FlakyTest(bugId = 178447631) + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, + secondaryApp.defaultWindowName) + ) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0) // bugId = 175687842 + supportedRotations = listOf(Surface.ROTATION_0) // b/175687842 ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt index 8737fc5f8430..ff8f9c6ed865 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt @@ -17,14 +17,15 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.os.Bundle +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.appWindowBecomesInVisible import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.helpers.reopenAppFromOverview import com.android.server.wm.flicker.layerBecomesInvisible @@ -35,6 +36,7 @@ import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEn import com.android.wm.shell.flicker.dockedStackDividerIsInvisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -46,56 +48,71 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class ExitPrimarySplitScreenShowSecondaryFullscreen( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testExitPrimarySplitScreenShowSecondaryFullscreen", configuration) - } - repeat { SplitScreenHelper.TEST_REPETITIONS } - teardown { - eachRun { - secondaryApp.exit(wmHelper) - } - } - transitions { - splitScreenApp.launchViaIntent(wmHelper) - secondaryApp.launchViaIntent(wmHelper) - device.launchSplitScreen(wmHelper) - device.reopenAppFromOverview(wmHelper) - // TODO(b/175687842) Can not find Split screen divider, use exit() instead - splitScreenApp.exit(wmHelper) - } - assertions { - layersTrace { - dockedStackDividerIsInvisible(bugId = 175687842) - layerBecomesInvisible(splitScreenApp.defaultWindowName) - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, - secondaryApp.defaultWindowName), - bugId = 178447631 - ) - } - windowManagerTrace { - appWindowBecomesInVisible(splitScreenApp.defaultWindowName) - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, - secondaryApp.defaultWindowName), - bugId = 178447631 - ) - } + testSpec: FlickerTestParameter +) : LegacySplitScreenTransition(testSpec) { + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { configuration -> + super.transition(this, configuration) + teardown { + eachRun { + secondaryApp.exit(wmHelper) } } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, defaultTransitionSetup, testSpec, + transitions { + splitScreenApp.launchViaIntent(wmHelper) + secondaryApp.launchViaIntent(wmHelper) + device.launchSplitScreen(wmHelper) + device.reopenAppFromOverview(wmHelper) + // TODO(b/175687842) Can not find Split screen divider, use exit() instead + splitScreenApp.exit(wmHelper) + } + } + + @FlakyTest(bugId = 175687842) + @Test + fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible() + + @Presubmit + @Test + fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName) + + @FlakyTest(bugId = 178447631) + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, + secondaryApp.defaultWindowName) + ) + + @Presubmit + @Test + fun appWindowBecomesInVisible() = + testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName) + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @FlakyTest(bugId = 178447631) + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, + secondaryApp.defaultWindowName) + ) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910 ) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt new file mode 100644 index 000000000000..893b101d0759 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.legacysplitscreen + +import android.os.Bundle +import android.view.Surface +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview +import com.android.server.wm.flicker.helpers.setRotation +import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen + +abstract class LegacySplitScreenRotateTransition( + testSpec: FlickerTestParameter +) : LegacySplitScreenTransition(testSpec) { + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { + setup { + eachRun { + device.wakeUpAndGoToHomeScreen() + device.openQuickStepAndClearRecentAppsFromOverview(wmHelper) + secondaryApp.launchViaIntent(wmHelper) + splitScreenApp.launchViaIntent(wmHelper) + } + } + teardown { + eachRun { + splitScreenApp.exit(wmHelper) + secondaryApp.exit(wmHelper) + this.setRotation(Surface.ROTATION_0) + } + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt index c0feaee73d9a..09a7e31d20e2 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt @@ -16,18 +16,19 @@ package com.android.wm.shell.flicker.legacysplitscreen +import android.os.Bundle import android.platform.test.annotations.Presubmit import android.support.test.launcherhelper.LauncherStrategyFactory import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.focusDoesNotChange -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.exitSplitScreen -import com.android.server.wm.flicker.helpers.isInSplitScreen import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview import com.android.server.wm.flicker.helpers.setRotation @@ -39,13 +40,13 @@ import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEnt import com.android.server.wm.flicker.layerBecomesInvisible import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.dockedStackDividerBecomesInvisible import com.android.wm.shell.flicker.helpers.SimpleAppHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -54,80 +55,100 @@ import org.junit.runners.Parameterized * Test open app to split screen. * To run this test: `atest WMShellFlickerTests:LegacySplitScreenToLauncher` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class LegacySplitScreenToLauncher( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val launcherPackageName = LauncherStrategyFactory.getInstance(instrumentation) - .launcherStrategy.supportedLauncherPackage - val testApp = SimpleAppHelper(instrumentation) + testSpec: FlickerTestParameter +) : LegacySplitScreenTransition(testSpec) { + private val launcherPackageName = LauncherStrategyFactory.getInstance(instrumentation) + .launcherStrategy.supportedLauncherPackage + private val testApp = SimpleAppHelper(instrumentation) - // b/161435597 causes the test not to work on 90 degrees - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - supportedRotations = listOf(Surface.ROTATION_0)) { configuration -> - withTestName { - buildTestTag("splitScreenToLauncher", configuration) + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { configuration -> + setup { + test { + device.wakeUpAndGoToHomeScreen() + device.openQuickStepAndClearRecentAppsFromOverview(wmHelper) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - device.openQuickStepAndClearRecentAppsFromOverview(wmHelper) - } - eachRun { - testApp.launchViaIntent(wmHelper) - this.setRotation(configuration.endRotation) - device.launchSplitScreen(wmHelper) - } + eachRun { + testApp.launchViaIntent(wmHelper) + this.setRotation(configuration.endRotation) + device.launchSplitScreen(wmHelper) + device.waitForIdle() } - teardown { - eachRun { - testApp.exit() - } - test { - if (device.isInSplitScreen()) { - device.exitSplitScreen() - } - } - } - transitions { - device.exitSplitScreen() - } - assertions { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry() - } - - layersTrace { - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - noUncoveredRegions(configuration.endRotation) - navBarLayerRotatesAndScales(configuration.endRotation) - statusBarLayerRotatesScales(configuration.endRotation) - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(launcherPackageName)) - - // b/161435597 causes the test not to work on 90 degrees - dockedStackDividerBecomesInvisible() - - layerBecomesInvisible(testApp.getPackage()) - } - - eventLog { - focusDoesNotChange(bugId = 151179149) - } + } + teardown { + eachRun { + testApp.exit(wmHelper) } } + transitions { + device.exitSplitScreen() + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.endRotation) + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.endRotation) + + @Presubmit + @Test + fun statusBarLayerRotatesScales() = + testSpec.statusBarLayerRotatesScales(testSpec.config.endRotation) + + @Presubmit + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(launcherPackageName)) + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun dockedStackDividerBecomesInvisible() = testSpec.dockedStackDividerBecomesInvisible() + + @Presubmit + @Test + fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(testApp.getPackage()) + + @FlakyTest(bugId = 151179149) + @Test + fun focusDoesNotChange() = testSpec.focusDoesNotChange() + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + // b/161435597 causes the test not to work on 90 degrees + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0)) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt index f9d2f49186a7..6ab1f0bfdb89 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt @@ -20,31 +20,28 @@ import android.app.Instrumentation import android.os.Bundle import android.support.test.launcherhelper.LauncherStrategyFactory import android.view.Surface +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.exitSplitScreen -import com.android.server.wm.flicker.helpers.isInSplitScreen +import com.android.server.wm.flicker.helpers.isRotated import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview -import com.android.server.wm.flicker.helpers.openQuickstep import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.wm.shell.flicker.helpers.SplitScreenHelper -abstract class LegacySplitScreenTransition( - protected val instrumentation: Instrumentation -) { - internal val splitScreenApp = SplitScreenHelper.getPrimary(instrumentation) - internal val secondaryApp = SplitScreenHelper.getSecondary(instrumentation) - internal val nonResizeableApp = SplitScreenHelper.getNonResizeable(instrumentation) - internal val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation) +abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestParameter) { + protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + protected val isRotated = testSpec.config.startRotation.isRotated() + protected val splitScreenApp = SplitScreenHelper.getPrimary(instrumentation) + protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation) + protected val nonResizeableApp = SplitScreenHelper.getNonResizeable(instrumentation) + protected val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation) .launcherStrategy.supportedLauncherPackage - internal val LIVE_WALLPAPER_PACKAGE_NAME = - "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2" - internal val LETTERBOX_NAME = "Letterbox" - internal val TOAST_NAME = "Toast" - internal val SPLASH_SCREEN_NAME = "Splash Screen" - internal open val defaultTransitionSetup: FlickerBuilder.(Bundle) -> Unit + protected open val transition: FlickerBuilder.(Bundle) -> Unit get() = { configuration -> setup { eachRun { @@ -57,17 +54,22 @@ abstract class LegacySplitScreenTransition( } teardown { eachRun { - // TODO(b/175687842) Workaround for exit legacy split screen - device.openQuickstep(wmHelper) - if (device.isInSplitScreen()) { - device.exitSplitScreen() - } - device.pressHome() + secondaryApp.exit(wmHelper) + splitScreenApp.exit(wmHelper) this.setRotation(Surface.ROTATION_0) } } } + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + transition(this, testSpec.config) + } + } + internal open val cleanSetup: FlickerBuilder.(Bundle) -> Unit get() = { configuration -> setup { @@ -79,37 +81,19 @@ abstract class LegacySplitScreenTransition( } teardown { eachRun { - // TODO(b/175687842) Workaround for exit legacy split screen - device.openQuickstep(wmHelper) - if (device.isInSplitScreen()) { - device.exitSplitScreen() - } + nonResizeableApp.exit(wmHelper) + splitScreenApp.exit(wmHelper) device.pressHome() this.setRotation(Surface.ROTATION_0) } } } - internal open val customRotateSetup: FlickerBuilder.(Bundle) -> Unit - get() = { configuration -> - setup { - eachRun { - device.wakeUpAndGoToHomeScreen() - device.openQuickStepAndClearRecentAppsFromOverview(wmHelper) - secondaryApp.launchViaIntent(wmHelper) - splitScreenApp.launchViaIntent(wmHelper) - } - } - teardown { - eachRun { - // TODO(b/175687842) Workaround for exit legacy split screen - device.openQuickstep(wmHelper) - if (device.isInSplitScreen()) { - device.exitSplitScreen() - } - device.pressHome() - this.setRotation(Surface.ROTATION_0) - } - } - } + companion object { + internal const val LIVE_WALLPAPER_PACKAGE_NAME = + "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2" + internal const val LETTERBOX_NAME = "Letterbox" + internal const val TOAST_NAME = "Toast" + internal const val SPLASH_SCREEN_NAME = "Splash Screen" + } } 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 a8de8db719a8..1b4b54a74eab 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 @@ -19,15 +19,15 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.os.Bundle import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.appWindowBecomesInVisible import com.android.server.wm.flicker.appWindowBecomesVisible import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.helpers.reopenAppFromOverview import com.android.server.wm.flicker.layerBecomesInvisible @@ -36,6 +36,7 @@ import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEnt import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -45,61 +46,73 @@ import org.junit.runners.Parameterized * (Non resizable activity launch via recent overview) * To run this test: `atest WMShellFlickerTests:NonResizableDismissInLegacySplitScreen` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class NonResizableDismissInLegacySplitScreen( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testNonResizableDismissInLegacySplitScreen", configuration) - } - repeat { SplitScreenHelper.TEST_REPETITIONS } - setup { - eachRun { - nonResizeableApp.launchViaIntent(wmHelper) - splitScreenApp.launchViaIntent(wmHelper) - device.launchSplitScreen(wmHelper) - } - } - transitions { - device.reopenAppFromOverview(wmHelper) - } - assertions { - layersTrace { - layerBecomesVisible(nonResizeableApp.defaultWindowName) - layerBecomesInvisible(splitScreenApp.defaultWindowName) - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, - LETTERBOX_NAME, TOAST_NAME, - splitScreenApp.defaultWindowName, - nonResizeableApp.defaultWindowName), - bugId = 178447631 - ) - } - windowManagerTrace { - appWindowBecomesVisible(nonResizeableApp.defaultWindowName) - appWindowBecomesInVisible(splitScreenApp.defaultWindowName) - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, - LETTERBOX_NAME, TOAST_NAME, - splitScreenApp.defaultWindowName, - nonResizeableApp.defaultWindowName), - bugId = 178447631 - ) - } + testSpec: FlickerTestParameter +) : LegacySplitScreenTransition(testSpec) { + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { configuration -> + cleanSetup(this, configuration) + setup { + eachRun { + nonResizeableApp.launchViaIntent(wmHelper) + splitScreenApp.launchViaIntent(wmHelper) + device.launchSplitScreen(wmHelper) } } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, cleanSetup, testSpec, + transitions { + device.reopenAppFromOverview(wmHelper) + } + } + + @Presubmit + @Test + fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName) + + @FlakyTest(bugId = 178447631) + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, + LETTERBOX_NAME, TOAST_NAME, + splitScreenApp.defaultWindowName, + nonResizeableApp.defaultWindowName) + ) + + @Presubmit + @Test + fun layerBecomesVisible() = testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName) + + @Presubmit + @Test + fun appWindowBecomesVisible() = + testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName) + + @Presubmit + @Test + fun appWindowBecomesInVisible() = + testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName) + + @FlakyTest(bugId = 178447631) + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( + listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, + LETTERBOX_NAME, TOAST_NAME, + splitScreenApp.defaultWindowName, + nonResizeableApp.defaultWindowName) + ) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */)) + supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 } } -}
\ No newline at end of file +} 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 c82c80237912..2365e3bfd8c3 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 @@ -19,15 +19,15 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.os.Bundle import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.appWindowBecomesInVisible import com.android.server.wm.flicker.appWindowBecomesVisible import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.layerBecomesInvisible import com.android.server.wm.flicker.layerBecomesVisible @@ -35,6 +35,7 @@ import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEnt import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -47,60 +48,73 @@ import org.junit.runners.Parameterized @Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class NonResizableLaunchInLegacySplitScreen( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testNonResizableLaunchInLegacySplitScreen", configuration) - } - repeat { SplitScreenHelper.TEST_REPETITIONS } - setup { - eachRun { - splitScreenApp.launchViaIntent(wmHelper) - device.launchSplitScreen(wmHelper) - } - } - transitions { - nonResizeableApp.launchViaIntent(wmHelper) - wmHelper.waitForAppTransitionIdle() - } - assertions { - layersTrace { - layerBecomesVisible(nonResizeableApp.defaultWindowName) - layerBecomesInvisible(splitScreenApp.defaultWindowName) - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(DOCKED_STACK_DIVIDER, - LAUNCHER_PACKAGE_NAME, - LETTERBOX_NAME, - nonResizeableApp.defaultWindowName, - splitScreenApp.defaultWindowName), - bugId = 178447631 - ) - } - windowManagerTrace { - appWindowBecomesVisible(nonResizeableApp.defaultWindowName) - appWindowBecomesInVisible(splitScreenApp.defaultWindowName) - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(DOCKED_STACK_DIVIDER, - LAUNCHER_PACKAGE_NAME, - LETTERBOX_NAME, - nonResizeableApp.defaultWindowName, - splitScreenApp.defaultWindowName), - bugId = 178447631 - ) - } + testSpec: FlickerTestParameter +) : LegacySplitScreenTransition(testSpec) { + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { configuration -> + cleanSetup(this, configuration) + setup { + eachRun { + splitScreenApp.launchViaIntent(wmHelper) + device.launchSplitScreen(wmHelper) } } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, cleanSetup, testSpec, + transitions { + nonResizeableApp.launchViaIntent(wmHelper) + wmHelper.waitForAppTransitionIdle() + } + } + + @Presubmit + @Test + fun layerBecomesVisible() = testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName) + + @Presubmit + @Test + fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName) + + @FlakyTest(bugId = 178447631) + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(DOCKED_STACK_DIVIDER, + LAUNCHER_PACKAGE_NAME, + LETTERBOX_NAME, + nonResizeableApp.defaultWindowName, + splitScreenApp.defaultWindowName) + ) + + @Presubmit + @Test + fun appWindowBecomesVisible() = + testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName) + + @Presubmit + @Test + fun appWindowBecomesInVisible() = + testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName) + + @FlakyTest(bugId = 178447631) + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( + listOf(DOCKED_STACK_DIVIDER, + LAUNCHER_PACKAGE_NAME, + LETTERBOX_NAME, + nonResizeableApp.defaultWindowName, + splitScreenApp.defaultWindowName) + ) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */)) + supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt index 9199c39535ac..b369a3d32e64 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt @@ -19,14 +19,14 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.os.Bundle import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.appWindowBecomesVisible import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.focusChanges -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.layerBecomesVisible import com.android.server.wm.flicker.noUncoveredRegions @@ -34,10 +34,10 @@ import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry -import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.appPairsDividerBecomesVisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -46,54 +46,66 @@ import org.junit.runners.Parameterized * Test open app to split screen. * To run this test: `atest WMShellFlickerTests:OpenAppToLegacySplitScreen` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class OpenAppToLegacySplitScreen( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val wmHelper = WindowManagerStateHelper() - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testOpenAppToLegacySplitScreen", configuration) - } - repeat { SplitScreenHelper.TEST_REPETITIONS } - transitions { - device.launchSplitScreen(wmHelper) - wmHelper.waitForAppTransitionIdle() - } - assertions { - windowManagerTrace { - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName), - bugId = 178447631) - appWindowBecomesVisible(splitScreenApp.getPackage()) - } + testSpec: FlickerTestParameter +) : LegacySplitScreenTransition(testSpec) { + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { configuration -> + super.transition(this, configuration) + transitions { + device.launchSplitScreen(wmHelper) + wmHelper.waitForAppTransitionIdle() + } + } - layersTrace { - noUncoveredRegions(configuration.startRotation, enabled = false) - statusBarLayerIsAlwaysVisible() - appPairsDividerBecomesVisible() - layerBecomesVisible(splitScreenApp.getPackage()) - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName), - bugId = 178447631) - } + @FlakyTest(bugId = 178447631) + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName) + ) - eventLog { - focusChanges(splitScreenApp.`package`, - "recents_animation_input_consumer", "NexusLauncherActivity", - bugId = 151179149) - } - } - } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, defaultTransitionSetup, testSpec, + @Presubmit + @Test + fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(splitScreenApp.getPackage()) + + @FlakyTest + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation) + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun appPairsDividerBecomesVisible() = testSpec.appPairsDividerBecomesVisible() + + @Presubmit + @Test + fun layerBecomesVisible() = testSpec.layerBecomesVisible(splitScreenApp.getPackage()) + + @FlakyTest(bugId = 178447631) + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName) + ) + + @FlakyTest(bugId = 151179149) + @Test + fun focusChanges() = testSpec.focusChanges(splitScreenApp.`package`, + "recents_animation_input_consumer", "NexusLauncherActivity") + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910 ) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt index c305bd856f58..ffffa1902976 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt @@ -16,24 +16,21 @@ package com.android.wm.shell.flicker.legacysplitscreen -import android.platform.test.annotations.Presubmit import android.graphics.Region +import android.os.Bundle import android.util.Rational import android.view.Surface import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.By import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.helpers.ImeAppHelper -import com.android.server.wm.flicker.focusDoesNotChange import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.helpers.buildTestTag -import com.android.server.wm.flicker.helpers.exitSplitScreen -import com.android.server.wm.flicker.helpers.isInSplitScreen import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.helpers.resizeSplitScreen import com.android.server.wm.flicker.helpers.setRotation @@ -42,7 +39,6 @@ import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales @@ -52,6 +48,7 @@ import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.traces.layers.getVisibleBounds import com.android.wm.shell.flicker.helpers.SimpleAppHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -62,14 +59,162 @@ import org.junit.runners.Parameterized * * Currently it runs only in 0 degrees because of b/156100803 */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @FlakyTest(bugId = 159096424) class ResizeLegacySplitScreen( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { + testSpec: FlickerTestParameter +) : LegacySplitScreenTransition(testSpec) { + private val testAppTop = SimpleAppHelper(instrumentation) + private val testAppBottom = ImeAppHelper(instrumentation) + + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { configuration -> + setup { + eachRun { + device.wakeUpAndGoToHomeScreen() + this.setRotation(configuration.startRotation) + this.launcherStrategy.clearRecentAppsFromOverview() + testAppBottom.launchViaIntent(wmHelper) + device.pressHome() + testAppTop.launchViaIntent(wmHelper) + device.waitForIdle() + device.launchSplitScreen(wmHelper) + val snapshot = + device.findObject(By.res(device.launcherPackageName, "snapshot")) + snapshot.click() + testAppBottom.openIME(device) + device.pressBack() + device.resizeSplitScreen(startRatio) + } + } + teardown { + eachRun { + testAppTop.exit(wmHelper) + testAppBottom.exit(wmHelper) + } + } + transitions { + device.resizeSplitScreen(stopRatio) + } + } + + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + + @FlakyTest(bugId = 156223549) + @Test + fun topAppWindowIsAlwaysVisible() { + testSpec.assertWm { + this.showsAppWindow(sSimpleActivity) + } + } + + @FlakyTest(bugId = 156223549) + @Test + fun bottomAppWindowIsAlwaysVisible() { + testSpec.assertWm { + this.showsAppWindow(sImeActivity) + } + } + + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.endRotation) + + @Test + fun navBarLayerRotatesAndScales() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.endRotation) + + @Test + fun statusBarLayerRotatesScales() = + testSpec.statusBarLayerRotatesScales(testSpec.config.endRotation) + + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + + @Test + fun topAppLayerIsAlwaysVisible() { + testSpec.assertLayers { + this.showsLayer(sSimpleActivity) + } + } + + @Test + fun bottomAppLayerIsAlwaysVisible() { + testSpec.assertLayers { + this.showsLayer(sImeActivity) + } + } + + @Test + fun dividerLayerIsAlwaysVisible() { + testSpec.assertLayers { + this.showsLayer(DOCKED_STACK_DIVIDER) + } + } + + @FlakyTest + @Test + fun appsStartingBounds() { + testSpec.assertLayersStart { + val displayBounds = WindowUtils.displayBounds + val dividerBounds = + entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds + + val topAppBounds = Region(0, 0, dividerBounds.right, + dividerBounds.top + WindowUtils.dockedStackDividerInset) + val bottomAppBounds = Region(0, + dividerBounds.bottom - WindowUtils.dockedStackDividerInset, + displayBounds.right, + displayBounds.bottom - WindowUtils.navigationBarHeight) + this.hasVisibleRegion("SimpleActivity", topAppBounds) + .hasVisibleRegion("ImeActivity", bottomAppBounds) + } + } + + @FlakyTest + @Test + fun appsEndingBounds() { + testSpec.assertLayersStart { + val displayBounds = WindowUtils.displayBounds + val dividerBounds = + entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds + + val topAppBounds = Region(0, 0, dividerBounds.right, + dividerBounds.top + WindowUtils.dockedStackDividerInset) + val bottomAppBounds = Region(0, + dividerBounds.bottom - WindowUtils.dockedStackDividerInset, + displayBounds.right, + displayBounds.bottom - WindowUtils.navigationBarHeight) + + this.hasVisibleRegion(sSimpleActivity, topAppBounds) + .hasVisibleRegion(sImeActivity, bottomAppBounds) + } + } + + @Test + fun focusDoesNotChange() { + testSpec.assertEventLog { + focusDoesNotChange() + } + } + companion object { private const val sSimpleActivity = "SimpleActivity" private const val sImeActivity = "ImeActivity" @@ -78,126 +223,14 @@ class ResizeLegacySplitScreen( @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testAppTop = SimpleAppHelper(instrumentation) - val testAppBottom = ImeAppHelper(instrumentation) - - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - supportedRotations = listOf(Surface.ROTATION_0)) { configuration -> - withTestName { - val description = (startRatio.toString().replace("/", "-") + "_to_" + - stopRatio.toString().replace("/", "-")) - buildTestTag("resizeSplitScreen", configuration, description) - } - repeat { configuration.repetitions } - setup { - eachRun { - device.wakeUpAndGoToHomeScreen() - this.setRotation(configuration.startRotation) - this.launcherStrategy.clearRecentAppsFromOverview() - testAppBottom.launchViaIntent(wmHelper) - device.pressHome() - testAppTop.launchViaIntent(wmHelper) - device.waitForIdle() - device.launchSplitScreen(wmHelper) - val snapshot = - device.findObject(By.res(device.launcherPackageName, "snapshot")) - snapshot.click() - testAppBottom.openIME(device) - device.pressBack() - device.resizeSplitScreen(startRatio) - } - } - teardown { - eachRun { - if (device.isInSplitScreen()) { - device.exitSplitScreen() - } - device.pressHome() - testAppTop.exit() - testAppBottom.exit() - } - test { - if (device.isInSplitScreen()) { - device.exitSplitScreen() - } - } - } - transitions { - device.resizeSplitScreen(stopRatio) - } - assertions { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry() - - all("topAppWindowIsAlwaysVisible", bugId = 156223549) { - this.showsAppWindow(sSimpleActivity) - } - - all("bottomAppWindowIsAlwaysVisible", bugId = 156223549) { - this.showsAppWindow(sImeActivity) - } - } - - layersTrace { - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - noUncoveredRegions(configuration.endRotation) - navBarLayerRotatesAndScales(configuration.endRotation) - statusBarLayerRotatesScales(configuration.endRotation) - visibleLayersShownMoreThanOneConsecutiveEntry() - - all("topAppLayerIsAlwaysVisible") { - this.showsLayer(sSimpleActivity) - } - - all("bottomAppLayerIsAlwaysVisible") { - this.showsLayer(sImeActivity) - } - - all("dividerLayerIsAlwaysVisible") { - this.showsLayer(DOCKED_STACK_DIVIDER) - } - - start("appsStartingBounds", enabled = false) { - val displayBounds = WindowUtils.displayBounds - val dividerBounds = - entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds - - val topAppBounds = Region(0, 0, dividerBounds.right, - dividerBounds.top + WindowUtils.dockedStackDividerInset) - val bottomAppBounds = Region(0, - dividerBounds.bottom - WindowUtils.dockedStackDividerInset, - displayBounds.right, - displayBounds.bottom - WindowUtils.navigationBarHeight) - this.hasVisibleRegion("SimpleActivity", topAppBounds) - .hasVisibleRegion("ImeActivity", bottomAppBounds) - } - - end("appsEndingBounds", enabled = false) { - val displayBounds = WindowUtils.displayBounds - val dividerBounds = - entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds - - val topAppBounds = Region(0, 0, dividerBounds.right, - dividerBounds.top + WindowUtils.dockedStackDividerInset) - val bottomAppBounds = Region(0, - dividerBounds.bottom - WindowUtils.dockedStackDividerInset, - displayBounds.right, - displayBounds.bottom - WindowUtils.navigationBarHeight) - - this.hasVisibleRegion(sSimpleActivity, topAppBounds) - .hasVisibleRegion(sImeActivity, bottomAppBounds) - } - } - - eventLog { - focusDoesNotChange() - } - } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0)) + .map { + val description = (startRatio.toString().replace("/", "-") + "_to_" + + stopRatio.toString().replace("/", "-")) + val newName = "${FlickerTestParameter.defaultName(it.config)}_$description" + FlickerTestParameter(it.config, name = newName) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt index 40bdaf3df5e8..c538008aa179 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt @@ -19,14 +19,14 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.os.Bundle import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.appWindowBecomesVisible 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.launchSplitScreen import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.navBarLayerRotatesAndScales @@ -38,6 +38,7 @@ import com.android.wm.shell.flicker.dockedStackDividerIsVisible import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -46,50 +47,64 @@ import org.junit.runners.Parameterized * Test dock activity to primary split screen and rotate * To run this test: `atest WMShellFlickerTests:RotateOneLaunchedAppAndEnterSplitScreen` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class RotateOneLaunchedAppAndEnterSplitScreen( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { + testSpec: FlickerTestParameter +) : LegacySplitScreenRotateTransition(testSpec) { + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { configuration -> + super.transition(this, configuration) + transitions { + device.launchSplitScreen(wmHelper) + this.setRotation(testSpec.config.startRotation) + } + } + + @FlakyTest(bugId = 175687842) + @Test + fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible() + + @FlakyTest(bugId = 175687842) + @Test + fun dockedStackPrimaryBoundsIsVisible() = + testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation, + splitScreenApp.defaultWindowName) + + @FlakyTest(bugId = 169271943) + @Test + fun navBarLayerRotatesAndScales() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, + testSpec.config.endRotation) + + @FlakyTest(bugId = 169271943) + @Test + fun statusBarLayerRotatesScales() = + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, + testSpec.config.endRotation) + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun appWindowBecomesVisible() = + testSpec.appWindowBecomesVisible(splitScreenApp.defaultWindowName) + + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testRotateOneLaunchedAppAndEnterSplitScreen", configuration) - } - repeat { SplitScreenHelper.TEST_REPETITIONS } - transitions { - device.launchSplitScreen(wmHelper) - this.setRotation(configuration.startRotation) - } - assertions { - layersTrace { - dockedStackDividerIsVisible(bugId = 175687842) - dockedStackPrimaryBoundsIsVisible( - configuration.startRotation, - splitScreenApp.defaultWindowName, bugId = 175687842) - navBarLayerRotatesAndScales( - configuration.startRotation, - configuration.endRotation, bugId = 169271943) - statusBarLayerRotatesScales( - configuration.startRotation, - configuration.endRotation, bugId = 169271943) - } - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - appWindowBecomesVisible(splitScreenApp.defaultWindowName) - } - } - } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, customRotateSetup, testSpec, - repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */)) + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(repetitions = SplitScreenHelper.TEST_REPETITIONS, + supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt index ae2c2d8f1bf2..c1162560119c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt @@ -19,14 +19,14 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.os.Bundle import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.appWindowBecomesVisible 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.launchSplitScreen import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.navBarLayerRotatesAndScales @@ -38,6 +38,7 @@ import com.android.wm.shell.flicker.dockedStackDividerIsVisible import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -46,50 +47,61 @@ import org.junit.runners.Parameterized * Rotate * To run this test: `atest WMShellFlickerTests:RotateOneLaunchedAppInSplitScreenMode` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class RotateOneLaunchedAppInSplitScreenMode( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { + testSpec: FlickerTestParameter +) : LegacySplitScreenRotateTransition(testSpec) { + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { configuration -> + super.transition(this, configuration) + transitions { + this.setRotation(testSpec.config.startRotation) + device.launchSplitScreen(wmHelper) + } + } + + @FlakyTest(bugId = 175687842) + @Test + fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible() + + @FlakyTest(bugId = 175687842) + @Test + fun dockedStackPrimaryBoundsIsVisible() = testSpec.dockedStackPrimaryBoundsIsVisible( + testSpec.config.startRotation, splitScreenApp.defaultWindowName) + + @FlakyTest(bugId = 169271943) + @Test + fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales( + testSpec.config.startRotation, testSpec.config.endRotation) + + @FlakyTest(bugId = 169271943) + @Test + fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales( + testSpec.config.startRotation, testSpec.config.endRotation) + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun appWindowBecomesVisible() = + testSpec.appWindowBecomesVisible(splitScreenApp.defaultWindowName) + + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testRotateOneLaunchedAppInSplitScreenMode", configuration) - } - repeat { SplitScreenHelper.TEST_REPETITIONS } - transitions { - this.setRotation(configuration.startRotation) - device.launchSplitScreen(wmHelper) - } - assertions { - layersTrace { - dockedStackDividerIsVisible(bugId = 175687842) - dockedStackPrimaryBoundsIsVisible( - configuration.startRotation, - splitScreenApp.defaultWindowName, bugId = 175687842) - navBarLayerRotatesAndScales( - configuration.startRotation, - configuration.endRotation, bugId = 169271943) - statusBarLayerRotatesScales( - configuration.startRotation, - configuration.endRotation, bugId = 169271943) - } - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - appWindowBecomesVisible(splitScreenApp.defaultWindowName) - } - } - } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, customRotateSetup, testSpec, + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */)) + supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt index aa9ac8f9782f..273925c0361c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt @@ -19,14 +19,14 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.os.Bundle import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.appWindowBecomesVisible 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.launchSplitScreen import com.android.server.wm.flicker.helpers.reopenAppFromOverview import com.android.server.wm.flicker.helpers.setRotation @@ -40,6 +40,7 @@ import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -48,54 +49,69 @@ import org.junit.runners.Parameterized * Test open app to split screen. * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppAndEnterSplitScreen` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class RotateTwoLaunchedAppAndEnterSplitScreen( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { + testSpec: FlickerTestParameter +) : LegacySplitScreenRotateTransition(testSpec) { + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { configuration -> + super.transition(this, configuration) + transitions { + this.setRotation(testSpec.config.startRotation) + device.launchSplitScreen(wmHelper) + device.reopenAppFromOverview(wmHelper) + } + } + + @FlakyTest(bugId = 175687842) + @Test + fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible() + + @FlakyTest(bugId = 175687842) + @Test + fun dockedStackPrimaryBoundsIsVisible() = + testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation, + splitScreenApp.defaultWindowName) + + @FlakyTest(bugId = 175687842) + @Test + fun dockedStackSecondaryBoundsIsVisible() = + testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation, + secondaryApp.defaultWindowName) + + @FlakyTest(bugId = 169271943) + @Test + fun navBarLayerRotatesAndScales() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, + testSpec.config.endRotation) + + @FlakyTest(bugId = 169271943) + @Test + fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales( + testSpec.config.startRotation, testSpec.config.endRotation) + + @Presubmit + @Test + fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp.defaultWindowName) + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testRotateTwoLaunchedAppAndEnterSplitScreen", configuration) - } - repeat { SplitScreenHelper.TEST_REPETITIONS } - transitions { - this.setRotation(configuration.startRotation) - device.launchSplitScreen(wmHelper) - device.reopenAppFromOverview(wmHelper) - } - assertions { - layersTrace { - dockedStackDividerIsVisible(bugId = 175687842) - dockedStackPrimaryBoundsIsVisible( - configuration.startRotation, - splitScreenApp.defaultWindowName, 175687842) - dockedStackSecondaryBoundsIsVisible( - configuration.startRotation, - secondaryApp.defaultWindowName, bugId = 175687842) - navBarLayerRotatesAndScales( - configuration.startRotation, - configuration.endRotation, bugId = 169271943) - statusBarLayerRotatesScales( - configuration.startRotation, - configuration.endRotation, bugId = 169271943) - } - windowManagerTrace { - appWindowBecomesVisible(secondaryApp.defaultWindowName) - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - } - } - } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, customRotateSetup, testSpec, + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */)) + supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt index 0e864dbbb75d..c7188dc227e7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt @@ -19,14 +19,14 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.os.Bundle import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.appWindowBecomesVisible 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.launchSplitScreen import com.android.server.wm.flicker.helpers.reopenAppFromOverview import com.android.server.wm.flicker.helpers.setRotation @@ -40,6 +40,7 @@ import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -48,59 +49,76 @@ import org.junit.runners.Parameterized * Test open app to split screen. * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppInSplitScreenMode` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class RotateTwoLaunchedAppInSplitScreenMode( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testRotateTwoLaunchedAppInSplitScreenMode", configuration) - } - repeat { SplitScreenHelper.TEST_REPETITIONS } - setup { - eachRun { - device.launchSplitScreen(wmHelper) - device.reopenAppFromOverview(wmHelper) - this.setRotation(configuration.startRotation) - } - } - transitions { - this.setRotation(configuration.startRotation) - } - assertions { - layersTrace { - dockedStackDividerIsVisible(bugId = 175687842) - dockedStackPrimaryBoundsIsVisible( - configuration.startRotation, - splitScreenApp.defaultWindowName, bugId = 175687842) - dockedStackSecondaryBoundsIsVisible( - configuration.startRotation, - secondaryApp.defaultWindowName, bugId = 175687842) - navBarLayerRotatesAndScales( - configuration.startRotation, - configuration.endRotation, bugId = 169271943) - statusBarLayerRotatesScales( - configuration.startRotation, - configuration.endRotation, bugId = 169271943) - } - windowManagerTrace { - appWindowBecomesVisible(secondaryApp.defaultWindowName) - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - } + testSpec: FlickerTestParameter +) : LegacySplitScreenRotateTransition(testSpec) { + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { configuration -> + super.transition(this, configuration) + setup { + eachRun { + device.launchSplitScreen(wmHelper) + device.reopenAppFromOverview(wmHelper) + this.setRotation(testSpec.config.startRotation) } } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, customRotateSetup, testSpec, + transitions { + this.setRotation(testSpec.config.startRotation) + } + } + + @FlakyTest(bugId = 175687842) + @Test + fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible() + + @FlakyTest(bugId = 175687842) + @Test + fun dockedStackPrimaryBoundsIsVisible() = + testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation, + splitScreenApp.defaultWindowName) + + @FlakyTest(bugId = 175687842) + @Test + fun dockedStackSecondaryBoundsIsVisible() = + testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation, + secondaryApp.defaultWindowName) + + @FlakyTest(bugId = 169271943) + @Test + fun navBarLayerRotatesAndScales() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, + testSpec.config.endRotation) + + @FlakyTest(bugId = 169271943) + @Test + fun statusBarLayerRotatesScales() = + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, + testSpec.config.endRotation) + + @Presubmit + @Test + fun appWindowBecomesVisible() = + testSpec.appWindowBecomesVisible(secondaryApp.defaultWindowName) + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */)) + supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt deleted file mode 100644 index bc42d5ed04ce..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.flicker.pip - -import android.os.SystemClock -import com.android.wm.shell.flicker.NonRotationTestBase - -abstract class AppTestBase( - rotationName: String, - rotation: Int -) : NonRotationTestBase(rotationName, rotation) { - companion object { - fun waitForAnimationComplete() { - // TODO: UiDevice doesn't have reliable way to wait for the completion of animation. - // Consider to introduce WindowManagerStateHelper to access Activity state. - SystemClock.sleep(1000) - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt index d56ed02972fb..ca48eaa45840 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt @@ -16,11 +16,14 @@ package com.android.wm.shell.flicker.pip +import android.os.Bundle +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.WindowUtils import com.android.wm.shell.flicker.helpers.FixedAppHelper import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible @@ -29,6 +32,7 @@ import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -39,64 +43,96 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class EnterExitPipTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): List<Array<Any>> { - val testApp = FixedAppHelper(instrumentation) - val testSpec = getTransition(eachRun = true) { configuration -> - setup { - eachRun { - testApp.launchViaIntent(wmHelper) - } - } - transitions { - // This will bring PipApp to fullscreen - pipApp.launchViaIntent(wmHelper) - } - assertions { - val displayBounds = WindowUtils.getDisplayBounds(configuration.startRotation) - presubmit { - windowManagerTrace { - all("pipApp must remain inside visible bounds") { - coversAtMostRegion(pipApp.defaultWindowName, displayBounds) - } - all("Initially shows both app windows then pipApp hides testApp") { - showsAppWindow(testApp.defaultWindowName) - .showsAppWindowOnTop(pipApp.defaultWindowName) - .then() - .hidesAppWindow(testApp.defaultWindowName) - } - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - } - layersTrace { - all("Initially shows both app layers then pipApp hides testApp") { - showsLayer(testApp.defaultWindowName) - .showsLayer(pipApp.defaultWindowName) - .then() - .hidesLayer(testApp.defaultWindowName) - } - start("testApp covers the fullscreen, pipApp remains inside display") { - hasVisibleRegion(testApp.defaultWindowName, displayBounds) - coversAtMostRegion(displayBounds, pipApp.defaultWindowName) - } - end("pipApp covers the fullscreen") { - hasVisibleRegion(pipApp.defaultWindowName, displayBounds) - } - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - } - } + testSpec: FlickerTestParameter +) : PipTransition(testSpec) { + private val testApp = FixedAppHelper(instrumentation) + private val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation) + + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = buildTransition(eachRun = true) { + setup { + eachRun { + testApp.launchViaIntent(wmHelper) } } - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - testSpec, supportedRotations = listOf(Surface.ROTATION_0), - repetitions = 5) + transitions { + // This will bring PipApp to fullscreen + pipApp.launchViaIntent(wmHelper) + } + } + + @Presubmit + @Test + fun pipAppRemainInsideVisibleBounds() { + testSpec.assertWm { + coversAtMostRegion(pipApp.defaultWindowName, displayBounds) + } + } + + @Presubmit + @Test + fun showBothAppWindowsThenHidePip() { + testSpec.assertWm { + showsAppWindow(testApp.defaultWindowName) + .showsAppWindowOnTop(pipApp.defaultWindowName) + .then() + .hidesAppWindow(testApp.defaultWindowName) + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun showBothAppLayersThenHidePip() { + testSpec.assertLayers { + showsLayer(testApp.defaultWindowName) + .showsLayer(pipApp.defaultWindowName) + .then() + .hidesLayer(testApp.defaultWindowName) + } + } + + @Presubmit + @Test + fun testAppCoversFullScreenWithPipOnDisplay() { + testSpec.assertLayersStart { + hasVisibleRegion(testApp.defaultWindowName, displayBounds) + coversAtMostRegion(displayBounds, pipApp.defaultWindowName) + } + } + + @Presubmit + @Test + fun pipAppCoversFullScreen() { + testSpec.assertLayersEnd { + hasVisibleRegion(pipApp.defaultWindowName, displayBounds) + } + } + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( + supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5) } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt index ff31ba7d2c01..e1fbc16e8ce2 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt @@ -16,11 +16,15 @@ package com.android.wm.shell.flicker.pip +import android.os.Bundle +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible @@ -30,6 +34,7 @@ import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.startRotation import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -40,58 +45,71 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class EnterPipTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): List<Array<Any>> { - val testSpec = getTransition(eachRun = true, - stringExtras = emptyMap()) { configuration -> - transitions { - pipApp.clickEnterPipButton() - pipApp.expandPipWindow(wmHelper) - } - assertions { - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() +class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = buildTransition(eachRun = true, stringExtras = emptyMap()) { + transitions { + pipApp.clickEnterPipButton() + pipApp.expandPipWindow(wmHelper) + } + } - all("pipWindowBecomesVisible") { - this.showsAppWindow(pipApp.defaultWindowName) - } - } + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - layersTrace { - statusBarLayerIsAlwaysVisible() - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) - } + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - layersTrace { - all("pipLayerBecomesVisible") { - this.showsLayer(pipApp.launcherName) - } - } - } + @Presubmit + @Test + fun pipWindowBecomesVisible() { + testSpec.assertWm { + this.showsAppWindow(pipApp.defaultWindowName) + } + } - flaky { - layersTrace { - navBarLayerIsAlwaysVisible(bugId = 140855415) - noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0) - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0, bugId = 140855415) - } - } - } - } + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarLayerRotatesScales() = + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - testSpec, supportedRotations = listOf(Surface.ROTATION_0), - repetitions = 5) + @Presubmit + @Test + fun pipLayerBecomesVisible() { + testSpec.assertLayers { + this.showsLayer(pipApp.launcherName) + } + } + + @FlakyTest(bugId = 140855415) + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @FlakyTest(bugId = 140855415) + @Test + fun navBarLayerRotatesAndScales() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + + @FlakyTest(bugId = 140855415) + @Test + fun noUncoveredRegions() = + testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigRotationTests(supportedRotations = listOf(Surface.ROTATION_0), + repetitions = 5) } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt index eaaa2f6390be..215b97bfeb83 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt @@ -16,22 +16,27 @@ package com.android.wm.shell.flicker.pip +import android.os.Bundle +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.wm.shell.flicker.helpers.FixedAppHelper +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.pip.PipTransitionBase.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE -import com.android.wm.shell.flicker.pip.PipTransitionBase.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT +import com.android.wm.shell.flicker.helpers.FixedAppHelper +import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE +import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -42,82 +47,108 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class EnterPipToOtherOrientationTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { - private val testApp = FixedAppHelper(instrumentation) + testSpec: FlickerTestParameter +) : PipTransition(testSpec) { + private val testApp = FixedAppHelper(instrumentation) + private val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90) + private val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0) - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - supportedRotations = listOf(Surface.ROTATION_0), - repetitions = 5) { configuration -> - setupAndTeardown(this, configuration) + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { configuration -> + setupAndTeardown(this, configuration) - setup { - eachRun { - // Launch a portrait only app on the fullscreen stack - testApp.launchViaIntent(wmHelper, stringExtras = mapOf( - EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString())) - // Launch the PiP activity fixed as landscape - pipApp.launchViaIntent(wmHelper, stringExtras = mapOf( - EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())) - } + setup { + eachRun { + // Launch a portrait only app on the fullscreen stack + testApp.launchViaIntent(wmHelper, stringExtras = mapOf( + EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString())) + // Launch the PiP activity fixed as landscape + pipApp.launchViaIntent(wmHelper, stringExtras = mapOf( + EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())) } - teardown { - eachRun { - pipApp.exit() - testApp.exit() - } - } - transitions { - // Enter PiP, and assert that the PiP is within bounds now that the device is back - // in portrait - broadcastActionTrigger.doAction(ACTION_ENTER_PIP) - wmHelper.waitPipWindowShown() - wmHelper.waitForAppTransitionIdle() + } + teardown { + eachRun { + pipApp.exit(wmHelper) + testApp.exit(wmHelper) } - assertions { - val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90) - val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0) + } + transitions { + // Enter PiP, and assert that the PiP is within bounds now that the device is back + // in portrait + broadcastActionTrigger.doAction(ACTION_ENTER_PIP) + wmHelper.waitPipWindowShown() + wmHelper.waitForAppTransitionIdle() + } + } - presubmit { - windowManagerTrace { - all("pipApp window is always on top") { - showsAppWindowOnTop(pipApp.defaultWindowName) - } - start("pipApp window hides testApp") { - isInvisible(testApp.defaultWindowName) - } - end("testApp windows is shown") { - isVisible(testApp.defaultWindowName) - } - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - } + @Presubmit + @Test + fun pipAppWindowIsAlwaysOnTop() { + testSpec.assertWm { + showsAppWindowOnTop(pipApp.defaultWindowName) + } + } - layersTrace { - start("pipApp layer hides testApp") { - hasVisibleRegion(pipApp.defaultWindowName, startingBounds) - isInvisible(testApp.defaultWindowName) - } - } - } + @Presubmit + @Test + fun pipAppHidesTestApp() { + testSpec.assertWmStart { + isInvisible(testApp.defaultWindowName) + } + } - flaky { - layersTrace { - end("testApp layer covers fullscreen") { - hasVisibleRegion(testApp.defaultWindowName, endingBounds) - } - navBarLayerIsAlwaysVisible(bugId = 140855415) - statusBarLayerIsAlwaysVisible(bugId = 140855415) - } - } - } - } + @Presubmit + @Test + fun testAppWindowIsVisible() { + testSpec.assertWmEnd { + isVisible(testApp.defaultWindowName) + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun pipAppLayerHidesTestApp() { + testSpec.assertLayersStart { + hasVisibleRegion(pipApp.defaultWindowName, startingBounds) + isInvisible(testApp.defaultWindowName) + } + } + + @FlakyTest + @Test + fun testAppLayerCoversFullScreen() { + testSpec.assertLayersEnd { + hasVisibleRegion(testApp.defaultWindowName, endingBounds) + } + } + + @FlakyTest(bugId = 140855415) + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @FlakyTest(bugId = 140855415) + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0), + repetitions = 5) } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt index f054e6412080..f3b9ea1455ca 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt @@ -16,17 +16,21 @@ package com.android.wm.shell.flicker.pip +import android.os.Bundle +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.startRotation import com.android.wm.shell.flicker.IME_WINDOW_NAME import com.android.wm.shell.flicker.helpers.ImeAppHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -37,58 +41,67 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class PipKeyboardTest(testSpec: FlickerTestRunnerFactory.TestSpec) : FlickerTestRunner(testSpec) { - companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { - private const val TAG_IME_VISIBLE = "imeIsVisible" +class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { + private val imeApp = ImeAppHelper(instrumentation) - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val imeApp = ImeAppHelper(instrumentation) - val testSpec = getTransition(eachRun = false) { configuration -> - setup { - test { - imeApp.launchViaIntent(wmHelper) - setRotation(configuration.startRotation) - } + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = buildTransition(eachRun = false) { configuration -> + setup { + test { + imeApp.launchViaIntent(wmHelper) + setRotation(configuration.startRotation) } - teardown { - test { - imeApp.exit() - setRotation(Surface.ROTATION_0) - } + } + teardown { + test { + imeApp.exit(wmHelper) + setRotation(Surface.ROTATION_0) } - transitions { - // open the soft keyboard - imeApp.openIME(wmHelper) - createTag(TAG_IME_VISIBLE) + } + transitions { + // open the soft keyboard + imeApp.openIME(wmHelper) + createTag(TAG_IME_VISIBLE) - // then close it again - imeApp.closeIME(wmHelper) - } - assertions { - presubmit { - windowManagerTrace { - // Ensure the pip window remains visible throughout - // any keyboard interactions - all("pipInVisibleBounds") { - val displayBounds = WindowUtils.getDisplayBounds( - configuration.startRotation) - coversAtMostRegion(pipApp.defaultWindowName, displayBounds) - } - // Ensure that the pip window does not obscure the keyboard - tag(TAG_IME_VISIBLE) { - isAboveWindow(IME_WINDOW_NAME, pipApp.defaultWindowName) - } - } - } - } + // then close it again + imeApp.closeIME(wmHelper) } + } + + /** + * Ensure the pip window remains visible throughout any keyboard interactions + */ + @Presubmit + @Test + fun pipInVisibleBounds() { + testSpec.assertWm { + val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation) + coversAtMostRegion(pipApp.defaultWindowName, displayBounds) + } + } + + /** + * Ensure that the pip window does not obscure the keyboard + */ + @Presubmit + @Test + fun pipIsAboveAppWindow() { + testSpec.assertWmTag(TAG_IME_VISIBLE) { + isAboveWindow(IME_WINDOW_NAME, pipApp.defaultWindowName) + } + } - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - testSpec, supportedRotations = listOf(Surface.ROTATION_0), - repetitions = 5) + companion object { + private const val TAG_IME_VISIBLE = "imeIsVisible" + + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0), + repetitions = 5) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt index 5a1e5a1fe7c5..daf381ee9ddb 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt @@ -16,21 +16,25 @@ package com.android.wm.shell.flicker.pip +import android.os.Bundle +import android.platform.test.annotations.Postsubmit import android.view.Surface import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.dsl.runFlicker +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.helpers.exitSplitScreen -import com.android.server.wm.flicker.helpers.isInSplitScreen import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.helpers.ImeAppHelper import com.android.wm.shell.flicker.helpers.FixedAppHelper -import com.android.wm.shell.flicker.helpers.PipAppHelper -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.repetitions +import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.removeAllTasksButHome import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP @@ -46,83 +50,103 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @FlakyTest(bugId = 161435597) -class PipLegacySplitScreenTest( - rotationName: String, - rotation: Int -) : AppTestBase(rotationName, rotation) { - private val pipApp = PipAppHelper(instrumentation) +class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { private val imeApp = ImeAppHelper(instrumentation) private val testApp = FixedAppHelper(instrumentation) + private val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation) - @Test - fun testShowsPipLaunchingToSplitScreen() { - runFlicker(instrumentation) { - withTestName { "testShowsPipLaunchingToSplitScreen" } - repeat { TEST_REPETITIONS } + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } setup { test { removeAllTasksButHome() device.wakeUpAndGoToHomeScreen() - pipApp.launchViaIntent(stringExtras = mapOf(EXTRA_ENTER_PIP to "true")) - waitForAnimationComplete() + pipApp.launchViaIntent(stringExtras = mapOf(EXTRA_ENTER_PIP to "true"), + wmHelper = wmHelper) } } transitions { - testApp.launchViaIntent() + testApp.launchViaIntent(wmHelper) device.launchSplitScreen(wmHelper) - imeApp.launchViaIntent() - waitForAnimationComplete() + imeApp.launchViaIntent(wmHelper) } teardown { eachRun { - imeApp.exit() - if (device.isInSplitScreen()) { - device.exitSplitScreen() - } - testApp.exit() + imeApp.exit(wmHelper) + testApp.exit(wmHelper) } test { removeAllTasksButHome() } } - assertions { - val displayBounds = WindowUtils.getDisplayBounds(rotation) - windowManagerTrace { - all("PIP window must remain inside visible bounds") { - coversAtMostRegion(pipApp.defaultWindowName, displayBounds) - } - end("Both app windows should be visible") { - isVisible(testApp.defaultWindowName) - isVisible(imeApp.defaultWindowName) - noWindowsOverlap(setOf(testApp.defaultWindowName, imeApp.defaultWindowName)) - } - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - } - layersTrace { - all("PIP layer must remain inside visible bounds") { - coversAtMostRegion(displayBounds, pipApp.defaultWindowName) - } - end("Both app layers should be visible") { - coversAtMostRegion(displayBounds, testApp.defaultWindowName) - coversAtMostRegion(displayBounds, imeApp.defaultWindowName) - } - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - } - } + } + + @Postsubmit + @Test + fun pipWindowInsideDisplayBounds() { + testSpec.assertWm { + coversAtMostRegion(pipApp.defaultWindowName, displayBounds) + } + } + + @Postsubmit + @Test + fun bothAppWindowsVisible() { + testSpec.assertWmEnd { + isVisible(testApp.defaultWindowName) + isVisible(imeApp.defaultWindowName) + noWindowsOverlap(setOf(testApp.defaultWindowName, imeApp.defaultWindowName)) + } + } + + @Postsubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + fun pipLayerInsideDisplayBounds() { + testSpec.assertLayers { + coversAtMostRegion(displayBounds, pipApp.defaultWindowName) + } + } + + @Postsubmit + @Test + fun bothAppLayersVisible() { + testSpec.assertLayersEnd { + coversAtMostRegion(displayBounds, testApp.defaultWindowName) + coversAtMostRegion(displayBounds, imeApp.defaultWindowName) } } + @Postsubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Postsubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + companion object { const val TEST_REPETITIONS = 2 + @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<Array<Any>> { - val supportedRotations = intArrayOf(Surface.ROTATION_0) - return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( + supportedRotations = listOf(Surface.ROTATION_0), + repetitions = TEST_REPETITIONS + ) } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt index ade65ac8aa63..43c12acef9e8 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt @@ -16,11 +16,15 @@ package com.android.wm.shell.flicker.pip +import android.os.Bundle +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.setRotation @@ -34,6 +38,7 @@ import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.statusBarLayerRotatesScales import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -44,73 +49,91 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class PipRotationTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val fixedApp = FixedAppHelper(instrumentation) - val testSpec = getTransition(eachRun = false) { configuration -> - setup { - test { - fixedApp.launchViaIntent(wmHelper) - } - eachRun { - setRotation(configuration.startRotation) - } +class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { + private val fixedApp = FixedAppHelper(instrumentation) + private val startingBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation) + private val endingBounds = WindowUtils.getDisplayBounds(testSpec.config.endRotation) + + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = buildTransition(eachRun = false) { configuration -> + setup { + test { + fixedApp.launchViaIntent(wmHelper) } - transitions { - setRotation(configuration.endRotation) + eachRun { + setRotation(configuration.startRotation) } - teardown { - eachRun { - setRotation(Surface.ROTATION_0) - } + } + transitions { + setRotation(configuration.endRotation) + } + teardown { + eachRun { + setRotation(Surface.ROTATION_0) } - assertions { - val startingBounds = WindowUtils.getDisplayBounds(configuration.startRotation) - val endingBounds = WindowUtils.getDisplayBounds(configuration.endRotation) + } + } - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - } + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - layersTrace { - noUncoveredRegions(configuration.startRotation, - configuration.endRotation, allStates = false) - } - } + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - flaky { - layersTrace { - navBarLayerIsAlwaysVisible(bugId = 140855415) - statusBarLayerIsAlwaysVisible(bugId = 140855415) - navBarLayerRotatesAndScales(configuration.startRotation, - configuration.endRotation, bugId = 140855415) - statusBarLayerRotatesScales(configuration.startRotation, - configuration.endRotation, bugId = 140855415) + @Presubmit + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, + testSpec.config.endRotation, allStates = false) - start("appLayerRotates_StartingBounds", bugId = 140855415) { - hasVisibleRegion(fixedApp.defaultWindowName, startingBounds) - coversAtMostRegion(startingBounds, pipApp.defaultWindowName) - } - end("appLayerRotates_EndingBounds", bugId = 140855415) { - hasVisibleRegion(fixedApp.defaultWindowName, endingBounds) - coversAtMostRegion(endingBounds, pipApp.defaultWindowName) - } - } - } - } - } + @FlakyTest(bugId = 140855415) + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @FlakyTest(bugId = 140855415) + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() - return FlickerTestRunnerFactory.getInstance().buildRotationTest(instrumentation, - testSpec, supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90), + @FlakyTest(bugId = 140855415) + @Test + fun navBarLayerRotatesAndScales() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, + testSpec.config.endRotation) + + @FlakyTest(bugId = 140855415) + @Test + fun statusBarLayerRotatesScales() = + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, + testSpec.config.endRotation) + + @FlakyTest(bugId = 140855415) + @Test + fun appLayerRotates_StartingBounds() { + testSpec.assertLayersStart { + hasVisibleRegion(fixedApp.defaultWindowName, startingBounds) + coversAtMostRegion(startingBounds, pipApp.defaultWindowName) + } + } + + @FlakyTest(bugId = 140855415) + @Test + fun appLayerRotates_EndingBounds() { + testSpec.assertLayersEnd { + hasVisibleRegion(fixedApp.defaultWindowName, endingBounds) + coversAtMostRegion(endingBounds, pipApp.defaultWindowName) + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigRotationTests( + supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90), repetitions = 5) } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt index 96b6c912d152..7ba085d3cf1a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt @@ -16,13 +16,14 @@ package com.android.wm.shell.flicker.pip +import com.android.wm.shell.flicker.FlickerTestBase import com.android.wm.shell.flicker.helpers.PipAppHelper import org.junit.Before abstract class PipTestBase( rotationName: String, rotation: Int -) : AppTestBase(rotationName, rotation) { +) : FlickerTestBase(rotationName, rotation) { protected val testApp = PipAppHelper(instrumentation) @Before diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt index f2d58997d1f2..02389a9ccf87 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt @@ -16,11 +16,15 @@ package com.android.wm.shell.flicker.pip +import android.os.Bundle +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.focusChanges import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible @@ -32,6 +36,7 @@ import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -42,73 +47,89 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class PipToAppTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): List<Array<Any>> { - val testSpec = getTransition(eachRun = true) { configuration -> - setup { - eachRun { - this.setRotation(configuration.startRotation) - } - } - teardown { - eachRun { - this.setRotation(Surface.ROTATION_0) - } +class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = buildTransition(eachRun = true) { configuration -> + setup { + eachRun { + this.setRotation(configuration.startRotation) } - transitions { - pipApp.expandPipWindowToApp(wmHelper) + } + teardown { + eachRun { + this.setRotation(Surface.ROTATION_0) } - assertions { - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() + } + transitions { + pipApp.expandPipWindowToApp(wmHelper) + } + } - all("appReplacesPipWindow") { - this.showsAppWindow(PIP_WINDOW_TITLE) - .then() - .showsAppWindowOnTop(pipApp.launcherName) - } - } + @FlakyTest(bugId = 140855415) + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - layersTrace { - statusBarLayerIsAlwaysVisible() - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() - all("appReplacesPipLayer") { - this.showsLayer(PIP_WINDOW_TITLE) - .then() - .showsLayer(pipApp.launcherName) - } - } - } + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - flaky { - layersTrace { - navBarLayerIsAlwaysVisible(bugId = 140855415) - noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0) - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0, bugId = 140855415) - } + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - eventLog { - focusChanges( - "NexusLauncherActivity", pipApp.launcherName, - "NexusLauncherActivity", bugId = 151179149) - } - } - } - } + @Presubmit + @Test + fun appReplacesPipWindow() { + testSpec.assertWm { + this.showsAppWindow(PIP_WINDOW_TITLE) + .then() + .showsAppWindowOnTop(pipApp.launcherName) + } + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales() = + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + + @Presubmit + @Test + fun appReplacesPipLayer() { + testSpec.assertLayers { + this.showsLayer(PIP_WINDOW_TITLE) + .then() + .showsLayer(pipApp.launcherName) + } + } - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - testSpec, supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5) + @FlakyTest + @Test + fun noUncoveredRegions() = + testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0) + + @FlakyTest(bugId = 140855415) + @Test + fun navBarLayerRotatesAndScales() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + + @FlakyTest(bugId = 151179149) + @Test + fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", + pipApp.launcherName, "NexusLauncherActivity") + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0), + repetitions = 5) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt index 1b44377425db..968a11de2511 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt @@ -16,15 +16,19 @@ package com.android.wm.shell.flicker.pip +import android.os.Bundle +import android.platform.test.annotations.Postsubmit +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.focusChanges import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.startRotation @@ -32,6 +36,7 @@ import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -42,74 +47,88 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class PipToHomeTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): List<Array<Any>> { - val testSpec = getTransition(eachRun = true) { configuration -> - setup { - eachRun { - this.setRotation(configuration.startRotation) - } - } - teardown { - eachRun { - this.setRotation(Surface.ROTATION_0) - } +class PipToHomeTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = buildTransition(eachRun = true) { configuration -> + setup { + eachRun { + this.setRotation(configuration.startRotation) } - transitions { - pipApp.closePipWindow(wmHelper) + } + teardown { + eachRun { + this.setRotation(Surface.ROTATION_0) } - assertions { - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() + } + transitions { + pipApp.closePipWindow(wmHelper) + } + } - all("pipWindowBecomesInvisible") { - this.showsAppWindow(PIP_WINDOW_TITLE) - .then() - .hidesAppWindow(PIP_WINDOW_TITLE) - } - } + @Postsubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - layersTrace { - statusBarLayerIsAlwaysVisible() - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() - all("pipLayerBecomesInvisible") { - this.showsLayer(PIP_WINDOW_TITLE) - .then() - .hidesLayer(PIP_WINDOW_TITLE) - } - } - } + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - postsubmit { - layersTrace { - navBarLayerIsAlwaysVisible() - noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0) - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0) - } - } + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - flaky { - eventLog { - focusChanges(pipApp.launcherName, "NexusLauncherActivity", - bugId = 151179149) - } - } - } - } + @Presubmit + @Test + fun pipWindowBecomesInvisible() { + testSpec.assertWm { + this.showsAppWindow(PIP_WINDOW_TITLE) + .then() + .hidesAppWindow(PIP_WINDOW_TITLE) + } + } + + @Presubmit + @Test + fun pipLayerBecomesInvisible() { + testSpec.assertLayers { + this.showsLayer(PIP_WINDOW_TITLE) + .then() + .hidesLayer(PIP_WINDOW_TITLE) + } + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales() = + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - testSpec, supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5) + @Postsubmit + @Test + fun noUncoveredRegions() = + testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0) + + @Postsubmit + @Test + fun navBarLayerRotatesAndScales() = + testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0) + + @FlakyTest(bugId = 151179149) + @Test + fun focusChanges() = testSpec.focusChanges(pipApp.launcherName, "NexusLauncherActivity") + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0), + repetitions = 5) } } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt index b1e404e4c8e6..a94483ec00a0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt @@ -20,16 +20,26 @@ import android.app.Instrumentation import android.content.Intent import android.os.Bundle import android.view.Surface +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.dsl.FlickerBuilder -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.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.repetitions +import com.android.server.wm.flicker.startRotation import com.android.wm.shell.flicker.helpers.PipAppHelper import com.android.wm.shell.flicker.removeAllTasksButHome import com.android.wm.shell.flicker.testapp.Components -abstract class PipTransitionBase(protected val instrumentation: Instrumentation) { +abstract class PipTransition(protected val testSpec: FlickerTestParameter) { + protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + protected val isRotated = testSpec.config.startRotation.isRotated() + protected val pipApp = PipAppHelper(instrumentation) + protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation) + protected abstract val transition: FlickerBuilder.(Bundle) -> Unit + // Helper class to process test actions by broadcast. protected class BroadcastActionTrigger(private val instrumentation: Instrumentation) { private fun createIntentWithAction(broadcastAction: String): Intent { @@ -59,16 +69,20 @@ abstract class PipTransitionBase(protected val instrumentation: Instrumentation) } } - protected val pipApp = PipAppHelper(instrumentation) - protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation) + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + transition(this, testSpec.config) + } + } /** * Gets a configuration that handles basic setup and teardown of pip tests */ protected val setupAndTeardown: FlickerBuilder.(Bundle) -> Unit - get() = { configuration -> - withTestName { buildTestTag(configuration) } - repeat { configuration.repetitions } + get() = { setup { test { removeAllTasksButHome() @@ -81,7 +95,7 @@ abstract class PipTransitionBase(protected val instrumentation: Instrumentation) } test { removeAllTasksButHome() - pipApp.exit() + pipApp.exit(wmHelper) } } } @@ -95,7 +109,7 @@ abstract class PipTransitionBase(protected val instrumentation: Instrumentation) * @param extraSpec Addicional segment of flicker specification */ @JvmOverloads - open fun getTransition( + protected open fun buildTransition( eachRun: Boolean, stringExtras: Map<String, String> = mapOf(Components.PipActivity.EXTRA_ENTER_PIP to "true"), extraSpec: FlickerBuilder.(Bundle) -> Unit = {} @@ -121,12 +135,12 @@ abstract class PipTransitionBase(protected val instrumentation: Instrumentation) teardown { eachRun { if (eachRun) { - pipApp.exit() + pipApp.exit(wmHelper) } } test { if (!eachRun) { - pipApp.exit() + pipApp.exit(wmHelper) } removeAllTasksButHome() } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt index c01bc94151e9..1f0370dc0505 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt @@ -16,21 +16,26 @@ package com.android.wm.shell.flicker.pip +import android.os.Bundle +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.pip.PipTransitionBase.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE +import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP import org.junit.Assert.assertEquals import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -41,75 +46,99 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class SetRequestedOrientationWhilePinnedTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - supportedRotations = listOf(Surface.ROTATION_0), - repetitions = 1) { configuration -> - setupAndTeardown(this, configuration) + testSpec: FlickerTestParameter +) : PipTransition(testSpec) { + private val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0) + private val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90) - setup { - eachRun { - // Launch the PiP activity fixed as landscape - pipApp.launchViaIntent(wmHelper, stringExtras = mapOf( - EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString(), - EXTRA_ENTER_PIP to "true")) - } - } - teardown { - eachRun { - pipApp.exit() - } - } - transitions { - // Request that the orientation is set to landscape - broadcastActionTrigger.requestOrientationForPip(ORIENTATION_LANDSCAPE) + override val transition: FlickerBuilder.(Bundle) -> Unit + get() = { configuration -> + setupAndTeardown(this, configuration) - // Launch the activity back into fullscreen and - // ensure that it is now in landscape - pipApp.launchViaIntent(wmHelper) - wmHelper.waitForFullScreenApp(pipApp.component) - wmHelper.waitForRotation(Surface.ROTATION_90) - assertEquals(Surface.ROTATION_90, device.displayRotation) + setup { + eachRun { + // Launch the PiP activity fixed as landscape + pipApp.launchViaIntent(wmHelper, stringExtras = mapOf( + EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString(), + EXTRA_ENTER_PIP to "true")) } - assertions { - val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0) - val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90) - presubmit { - windowManagerTrace { - start("PIP window must remain inside display") { - coversAtMostRegion(pipApp.defaultWindowName, startingBounds) - } - end("pipApp shows on top") { - showsAppWindowOnTop(pipApp.defaultWindowName) - } - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - } - layersTrace { - start("PIP layer must remain inside display") { - coversAtMostRegion(startingBounds, pipApp.defaultWindowName) - } - end("pipApp layer covers fullscreen") { - hasVisibleRegion(pipApp.defaultWindowName, endingBounds) - } - } - } - - flaky { - layersTrace { - navBarLayerIsAlwaysVisible(bugId = 140855415) - statusBarLayerIsAlwaysVisible(bugId = 140855415) - } - } + } + teardown { + eachRun { + pipApp.exit(wmHelper) } } + transitions { + // Request that the orientation is set to landscape + broadcastActionTrigger.requestOrientationForPip(ORIENTATION_LANDSCAPE) + + // Launch the activity back into fullscreen and + // ensure that it is now in landscape + pipApp.launchViaIntent(wmHelper) + wmHelper.waitForFullScreenApp(pipApp.component) + wmHelper.waitForRotation(Surface.ROTATION_90) + assertEquals(Surface.ROTATION_90, device.displayRotation) + } + } + + @Presubmit + @Test + fun pipWindowInsideDisplay() { + testSpec.assertWmStart { + coversAtMostRegion(pipApp.defaultWindowName, startingBounds) + } + } + + @Presubmit + @Test + fun pipAppShowsOnTop() { + testSpec.assertWmEnd { + showsAppWindowOnTop(pipApp.defaultWindowName) + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun pipLayerInsideDisplay() { + testSpec.assertLayersStart { + coversAtMostRegion(startingBounds, pipApp.defaultWindowName) + } + } + + @Presubmit + @Test + fun pipAppLayerCoversFullScreen() { + testSpec.assertLayersEnd { + hasVisibleRegion(pipApp.defaultWindowName, endingBounds) + } + } + + @FlakyTest(bugId = 140855415) + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @FlakyTest(bugId = 140855415) + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0), + repetitions = 1) } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp index 26627a47ee62..ea606df1536d 100644 --- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test { name: "WMShellFlickerTestApp", srcs: ["**/*.java"], @@ -23,4 +32,4 @@ java_library { name: "wmshell-flicker-test-components", srcs: ["src/**/Components.java"], sdk_version: "test_current", -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java index e094158e1144..27c626170a4b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java @@ -20,17 +20,17 @@ import static org.mockito.Mockito.mock; 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 com.android.wm.shell.common.SyncTransactionQueue; -import org.mockito.Mock; - public class TestAppPairsController extends AppPairsController { private TestAppPairsPool mPool; public TestAppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue, DisplayController displayController) { - super(organizer, syncQueue, displayController, mock(ShellExecutor.class)); + super(organizer, syncQueue, displayController, mock(ShellExecutor.class), + mock(DisplayImeController.class)); mPool = new TestAppPairsPool(this); setPairsPool(mPool); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt index 416028088294..bdf75fcd8816 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.bubbles.storage +import android.app.ActivityTaskManager.INVALID_TASK_ID import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.wm.shell.ShellTestCase @@ -31,9 +32,10 @@ import org.junit.runner.RunWith class BubblePersistentRepositoryTest : ShellTestCase() { private val bubbles = listOf( - BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0), - BubbleEntity(10, "com.example.chat", "alice and bob", "key-2", 0, 16537428, "title"), - BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0) + BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0, null, 1), + BubbleEntity(10, "com.example.chat", "alice and bob", "key-2", 0, 16537428, "title", 2), + BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0, null, + INVALID_TASK_ID) ) private lateinit var repository: BubblePersistentRepository diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt index dd1a6a5a281e..05795fde7d6c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.bubbles.storage +import android.app.ActivityTaskManager.INVALID_TASK_ID import android.content.pm.LauncherApps import android.os.UserHandle import android.testing.AndroidTestingRunner @@ -37,10 +38,12 @@ class BubbleVolatileRepositoryTest : ShellTestCase() { private val user0 = UserHandle.of(0) private val user10 = UserHandle.of(10) - private val bubble1 = BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0) + private val bubble1 = BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0, + null, 1) private val bubble2 = BubbleEntity(10, "com.example.chat", "alice and bob", - "key-2", 0, 16537428, "title") - private val bubble3 = BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0) + "key-2", 0, 16537428, "title", 2) + private val bubble3 = BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0, + null, INVALID_TASK_ID) private val bubbles = listOf(bubble1, bubble2, bubble3) @@ -105,13 +108,13 @@ class BubbleVolatileRepositoryTest : ShellTestCase() { @Test fun testAddBubbleMatchesByKey() { - val bubble = BubbleEntity(0, "com.example.pkg", "shortcut-id", "key", 120, 0, "title") + val bubble = BubbleEntity(0, "com.example.pkg", "shortcut-id", "key", 120, 0, "title", 1) repository.addBubbles(listOf(bubble)) assertEquals(bubble, repository.bubbles.get(0)) // Same key as first bubble but different entry val bubbleModified = BubbleEntity(0, "com.example.pkg", "shortcut-id", "key", 120, 0, - "different title") + "different title", 2) repository.addBubbles(listOf(bubbleModified)) assertEquals(bubbleModified, repository.bubbles.get(0)) } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt index e0891a95c6a6..839b873d0c23 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.bubbles.storage +import android.app.ActivityTaskManager.INVALID_TASK_ID import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.wm.shell.ShellTestCase @@ -31,17 +32,18 @@ import java.io.ByteArrayOutputStream class BubbleXmlHelperTest : ShellTestCase() { private val bubbles = listOf( - BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1", 120, 0), - BubbleEntity(10, "com.example.chat", "alice and bob", "k2", 0, 16537428, "title"), - BubbleEntity(0, "com.example.messenger", "shortcut-2", "k3", 120, 0) + BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1", 120, 0, null, 1), + BubbleEntity(10, "com.example.chat", "alice and bob", "k2", 0, 16537428, "title", 2), + BubbleEntity(0, "com.example.messenger", "shortcut-2", "k3", 120, 0, null, + INVALID_TASK_ID) ) @Test fun testWriteXml() { val expectedEntries = """ -<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" /> -<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" /> -<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" /> +<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" tid="1" /> +<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" tid="2" /> +<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" tid="-1" /> """.trimIndent() ByteArrayOutputStream().use { writeXml(it, bubbles) @@ -56,9 +58,9 @@ class BubbleXmlHelperTest : ShellTestCase() { val src = """ <?xml version='1.0' encoding='utf-8' standalone='yes' ?> <bs v="1"> -<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" /> -<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" /> -<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" /> +<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" tid="1" /> +<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" tid="2" /> +<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" tid="-1" /> </bs> """.trimIndent() val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8))) @@ -79,4 +81,32 @@ class BubbleXmlHelperTest : ShellTestCase() { val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8))) assertEquals("failed parsing bubbles from xml\n$src", emptyList<BubbleEntity>(), actual) } + + /** + * In S we changed the XML to include a taskId, version didn't increase because we can set a + * reasonable default for taskId (INVALID_TASK_ID) if it wasn't in the XML previously, this + * tests that that works. + */ + @Test + fun testReadXMLWithoutTaskId() { + val expectedBubbles = listOf( + BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1", 120, 0, null, + INVALID_TASK_ID), + BubbleEntity(10, "com.example.chat", "alice and bob", "k2", 0, 16537428, "title", + INVALID_TASK_ID), + BubbleEntity(0, "com.example.messenger", "shortcut-2", "k3", 120, 0, null, + INVALID_TASK_ID) + ) + + val src = """ +<?xml version='1.0' encoding='utf-8' standalone='yes' ?> +<bs v="1"> +<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" /> +<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" /> +<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" /> +</bs> + """.trimIndent() + val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8))) + assertEquals("failed parsing bubbles from xml\n$src", expectedBubbles, actual) + } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java index 2b5b77e49e3a..88e754c58792 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java @@ -38,6 +38,12 @@ import com.android.internal.R; import org.junit.Test; +/** + * Tests for {@link DisplayLayout}. + * + * Build/Install/Run: + * atest WMShellUnitTests:DisplayLayoutTest + */ @SmallTest public class DisplayLayoutTest { @@ -70,18 +76,6 @@ public class DisplayLayoutTest { @Test public void testRotate() { // Basic rotate utility - Rect testParent = new Rect(0, 0, 1000, 600); - Rect testInner = new Rect(40, 20, 120, 80); - Rect testResult = new Rect(testInner); - DisplayLayout.rotateBounds(testResult, testParent, 1); - assertEquals(new Rect(20, 880, 80, 960), testResult); - testResult.set(testInner); - DisplayLayout.rotateBounds(testResult, testParent, 2); - assertEquals(new Rect(880, 20, 960, 80), testResult); - testResult.set(testInner); - DisplayLayout.rotateBounds(testResult, testParent, 3); - assertEquals(new Rect(520, 40, 580, 120), testResult); - Resources res = createResources(40, 50, false, 30, 40); DisplayInfo info = createDisplayInfo(1000, 1500, 60, ROTATION_0); DisplayLayout dl = new DisplayLayout(info, res, true, true); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java index 21bc32c6563c..d8aebc284bf1 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java @@ -198,7 +198,7 @@ public class TaskStackListenerImplTest { @Test public void testOnActivityDismissingDockedStack() { - mImpl.onActivityDismissingDockedStack(); + mImpl.onActivityDismissingDockedTask(); verify(mCallback).onActivityDismissingDockedStack(); verify(mOtherCallback).onActivityDismissingDockedStack(); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java index 5821eed6f611..7b0e6b9a5ed7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java @@ -36,6 +36,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.policy.DividerSnapAlgorithm; import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.common.DisplayImeController; import org.junit.Before; import org.junit.Test; @@ -49,6 +50,7 @@ import org.mockito.MockitoAnnotations; public class SplitLayoutTests extends ShellTestCase { @Mock SplitLayout.LayoutChangeListener mLayoutChangeListener; @Mock SurfaceControl mRootLeash; + @Mock DisplayImeController mDisplayImeController; private SplitLayout mSplitLayout; @Before @@ -59,7 +61,8 @@ public class SplitLayoutTests extends ShellTestCase { mContext, getConfiguration(false), mLayoutChangeListener, - b -> b.setParent(mRootLeash)); + b -> b.setParent(mRootLeash), + mDisplayImeController); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java index 698315a77d8e..86d0d82222e4 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java @@ -29,6 +29,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.common.DisplayImeController; import org.junit.Before; import org.junit.Test; @@ -42,6 +43,7 @@ import org.mockito.MockitoAnnotations; public class SplitWindowManagerTests extends ShellTestCase { @Mock SurfaceControl mSurfaceControl; @Mock SplitLayout mSplitLayout; + @Mock DisplayImeController mDisplayImeController; private SplitWindowManager mSplitWindowManager; @Before @@ -50,7 +52,7 @@ public class SplitWindowManagerTests extends ShellTestCase { final Configuration configuration = new Configuration(); configuration.setToDefaults(); mSplitWindowManager = new SplitWindowManager("TestSplitDivider", mContext, configuration, - b -> b.setParent(mSurfaceControl)); + b -> b.setParent(mSurfaceControl), mDisplayImeController); when(mSplitLayout.getDividerBounds()).thenReturn( new Rect(0, 0, configuration.windowConfiguration.getBounds().width(), configuration.windowConfiguration.getBounds().height())); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java index 3147dab1a0f8..63b94139dd9c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java @@ -16,6 +16,7 @@ package com.android.wm.shell.pip; +import static android.util.RotationUtils.rotateBounds; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_90; @@ -37,7 +38,6 @@ import android.view.SurfaceControl; import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTestCase; -import com.android.wm.shell.common.DisplayLayout; import org.junit.Before; import org.junit.Test; @@ -141,7 +141,7 @@ public class PipAnimationControllerTest extends ShellTestCase { // Apply fraction 1 to compute the end value. animator.applySurfaceControlTransaction(mLeash, new DummySurfaceControlTx(), 1); final Rect rotatedEndBounds = new Rect(endBounds); - DisplayLayout.rotateBounds(rotatedEndBounds, endBounds, ROTATION_90); + rotateBounds(rotatedEndBounds, endBounds, ROTATION_90); assertEquals("Expect 90 degree rotated bounds", rotatedEndBounds, animator.mCurrentValue); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java index cfe84639d24e..f2b4e9761226 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java @@ -124,7 +124,7 @@ public class PipControllerTest extends ShellTestCase { final ComponentName component1 = new ComponentName(mContext, "component1"); when(mMockPipBoundsState.getLastPipComponentName()).thenReturn(component1); - mPipController.mPinnedStackListener.onActivityHidden(component1); + mPipController.mPinnedTaskListener.onActivityHidden(component1); verify(mMockPipBoundsState).setLastPipComponentName(null); } @@ -135,7 +135,7 @@ public class PipControllerTest extends ShellTestCase { final ComponentName component2 = new ComponentName(mContext, "component2"); when(mMockPipBoundsState.getLastPipComponentName()).thenReturn(component1); - mPipController.mPinnedStackListener.onActivityHidden(component2); + mPipController.mPinnedTaskListener.onActivityHidden(component2); verify(mMockPipBoundsState, never()).setLastPipComponentName(null); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index d2d18129d071..74753aac4a24 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -41,6 +41,7 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; +import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.SyncTransactionQueue; import org.junit.Before; @@ -58,13 +59,14 @@ public class StageCoordinatorTests extends ShellTestCase { @Mock private RootTaskDisplayAreaOrganizer mRootTDAOrganizer; @Mock private MainStage mMainStage; @Mock private SideStage mSideStage; + @Mock private DisplayImeController mDisplayImeController; private StageCoordinator mStageCoordinator; @Before public void setup() { MockitoAnnotations.initMocks(this); mStageCoordinator = new TestStageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, - mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage); + mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage, mDisplayImeController); } @Test @@ -94,9 +96,9 @@ public class StageCoordinatorTests extends ShellTestCase { TestStageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer, - MainStage mainStage, SideStage sideStage) { + MainStage mainStage, SideStage sideStage, DisplayImeController imeController) { super(context, displayId, syncQueue, rootTDAOrganizer, taskOrganizer, mainStage, - sideStage); + sideStage, imeController); // Prepare default TaskDisplayArea for testing. mDisplayAreaInfo = new DisplayAreaInfo( diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index 5eca3e75a7b8..926108c41e5e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -41,6 +41,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.app.ActivityManager.RunningTaskInfo; +import android.content.Context; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -58,6 +59,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.ShellExecutor; @@ -78,6 +80,8 @@ public class ShellTransitionTests { private final WindowOrganizer mOrganizer = mock(WindowOrganizer.class); private final TransactionPool mTransactionPool = mock(TransactionPool.class); + private final Context mContext = + InstrumentationRegistry.getInstrumentation().getTargetContext(); private final TestShellExecutor mMainExecutor = new TestShellExecutor(); private final ShellExecutor mAnimExecutor = new TestShellExecutor(); private final TestTransitionHandler mDefaultHandler = new TestTransitionHandler(); @@ -90,8 +94,8 @@ public class ShellTransitionTests { @Test public void testBasicTransitionFlow() { - Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor, - mAnimExecutor); + Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext, + mMainExecutor, mAnimExecutor); transitions.replaceDefaultHandlerForTest(mDefaultHandler); IBinder transitToken = new Binder(); @@ -109,8 +113,8 @@ public class ShellTransitionTests { @Test public void testNonDefaultHandler() { - Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor, - mAnimExecutor); + Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext, + mMainExecutor, mAnimExecutor); transitions.replaceDefaultHandlerForTest(mDefaultHandler); final WindowContainerTransaction handlerWCT = new WindowContainerTransaction(); @@ -188,8 +192,8 @@ public class ShellTransitionTests { @Test public void testRequestRemoteTransition() { - Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor, - mAnimExecutor); + Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext, + mMainExecutor, mAnimExecutor); transitions.replaceDefaultHandlerForTest(mDefaultHandler); final boolean[] remoteCalled = new boolean[]{false}; @@ -255,8 +259,8 @@ public class ShellTransitionTests { @Test public void testRegisteredRemoteTransition() { - Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor, - mAnimExecutor); + Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext, + mMainExecutor, mAnimExecutor); transitions.replaceDefaultHandlerForTest(mDefaultHandler); final boolean[] remoteCalled = new boolean[]{false}; diff --git a/libs/androidfw/TEST_MAPPING b/libs/androidfw/TEST_MAPPING index 777aa0b429e5..766714cd7694 100644 --- a/libs/androidfw/TEST_MAPPING +++ b/libs/androidfw/TEST_MAPPING @@ -1,10 +1,6 @@ { "presubmit": [ { - "name": "libandroidfw_tests", - "host": true - }, - { "name": "CtsResourcesLoaderTests" } ] diff --git a/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp b/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp index b36ff0968ba3..3035a79f668d 100644 --- a/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp +++ b/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_libs_androidfw_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_libs_androidfw_license"], +} + cc_fuzz { name: "cursorwindow_fuzzer", srcs: [ diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp index 5d3f6f2f28c9..2448cc904104 100644 --- a/libs/hwui/FrameInfo.cpp +++ b/libs/hwui/FrameInfo.cpp @@ -20,13 +20,12 @@ namespace android { namespace uirenderer { -const std::array<std::string, static_cast<int>(FrameInfoIndex::NumIndexes)> FrameInfoNames = { +const std::array FrameInfoNames{ "Flags", "FrameTimelineVsyncId", "IntendedVsync", "Vsync", - "OldestInputEvent", - "NewestInputEvent", + "InputEventId", "HandleInputStart", "AnimationStart", "PerformTraversalsStart", @@ -40,7 +39,8 @@ const std::array<std::string, static_cast<int>(FrameInfoIndex::NumIndexes)> Fram "DequeueBufferDuration", "QueueBufferDuration", "GpuCompleted", - "SwapBuffersCompleted" + "SwapBuffersCompleted", + "DisplayPresentTime", }; static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 20, diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h index 45a367f525da..912d04c5d87d 100644 --- a/libs/hwui/FrameInfo.h +++ b/libs/hwui/FrameInfo.h @@ -28,15 +28,14 @@ namespace android { namespace uirenderer { -#define UI_THREAD_FRAME_INFO_SIZE 11 +static constexpr size_t UI_THREAD_FRAME_INFO_SIZE = 10; enum class FrameInfoIndex { Flags = 0, FrameTimelineVsyncId, IntendedVsync, Vsync, - OldestInputEvent, - NewestInputEvent, + InputEventId, HandleInputStart, AnimationStart, PerformTraversalsStart, @@ -56,13 +55,14 @@ enum class FrameInfoIndex { GpuCompleted, SwapBuffersCompleted, + DisplayPresentTime, // Must be the last value! // Also must be kept in sync with FrameMetrics.java#FRAME_STATS_COUNT NumIndexes }; -extern const std::array<std::string, static_cast<int>(FrameInfoIndex::NumIndexes)> FrameInfoNames; +extern const std::array<const char*, static_cast<int>(FrameInfoIndex::NumIndexes)> FrameInfoNames; namespace FrameInfoFlags { enum { diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp index 2cd9b7b39174..4eefe921fbe9 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.cpp @@ -112,7 +112,7 @@ void JankTracker::finishFrame(const FrameInfo& frame) { std::lock_guard lock(mDataMutex); // Fast-path for jank-free frames - int64_t totalDuration = frame.duration(sFrameStart, FrameInfoIndex::FrameCompleted); + int64_t totalDuration = frame.duration(sFrameStart, FrameInfoIndex::SwapBuffersCompleted); if (mDequeueTimeForgiveness && frame[FrameInfoIndex::DequeueBufferDuration] > 500_us) { nsecs_t expectedDequeueDuration = mDequeueTimeForgiveness + frame[FrameInfoIndex::Vsync] - frame[FrameInfoIndex::IssueDrawCommandsStart]; @@ -219,7 +219,7 @@ void JankTracker::dumpData(int fd, const ProfileDataDescription* description, void JankTracker::dumpFrames(int fd) { dprintf(fd, "\n\n---PROFILEDATA---\n"); for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) { - dprintf(fd, "%s", FrameInfoNames[i].c_str()); + dprintf(fd, "%s", FrameInfoNames[i]); dprintf(fd, ","); } for (size_t i = 0; i < mFrames.size(); i++) { diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 1fddac4cd05d..28d2b4cec0e1 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -188,7 +188,7 @@ void SkiaCanvas::restoreUnclippedLayer(int restoreCount, const SkPaint& paint) { } if (mCanvas->getSaveCount() == restoreCount + 1) { - SkCanvasPriv::DrawBehind(mCanvas, *filterPaint(paint)); + SkCanvasPriv::DrawBehind(mCanvas, filterPaint(paint)); this->restore(); } } @@ -431,15 +431,14 @@ void SkiaCanvas::drawColor(int color, SkBlendMode mode) { mCanvas->drawColor(color, mode); } -SkiaCanvas::PaintCoW&& SkiaCanvas::filterPaint(PaintCoW&& paint) const { +void SkiaCanvas::onFilterPaint(SkPaint& paint) { if (mPaintFilter) { - mPaintFilter->filter(&paint.writeable()); + mPaintFilter->filter(&paint); } - return std::move(paint); } void SkiaCanvas::drawPaint(const SkPaint& paint) { - mCanvas->drawPaint(*filterPaint(paint)); + mCanvas->drawPaint(filterPaint(paint)); } // ---------------------------------------------------------------------------- @@ -457,13 +456,11 @@ void SkiaCanvas::drawPoints(const float* points, int count, const Paint& paint, points += 2; } - apply_looper(&paint, [&](const SkPaint& p) { - mCanvas->drawPoints(mode, count, pts.get(), p); - }); + applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawPoints(mode, count, pts.get(), p); }); } void SkiaCanvas::drawPoint(float x, float y, const Paint& paint) { - apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawPoint(x, y, p); }); + applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawPoint(x, y, p); }); } void SkiaCanvas::drawPoints(const float* points, int count, const Paint& paint) { @@ -472,9 +469,8 @@ void SkiaCanvas::drawPoints(const float* points, int count, const Paint& paint) void SkiaCanvas::drawLine(float startX, float startY, float stopX, float stopY, const Paint& paint) { - apply_looper(&paint, [&](const SkPaint& p) { - mCanvas->drawLine(startX, startY, stopX, stopY, p); - }); + applyLooper(&paint, + [&](const SkPaint& p) { mCanvas->drawLine(startX, startY, stopX, stopY, p); }); } void SkiaCanvas::drawLines(const float* points, int count, const Paint& paint) { @@ -484,46 +480,44 @@ void SkiaCanvas::drawLines(const float* points, int count, const Paint& paint) { void SkiaCanvas::drawRect(float left, float top, float right, float bottom, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; - apply_looper(&paint, [&](const SkPaint& p) { + applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawRect({left, top, right, bottom}, p); }); } void SkiaCanvas::drawRegion(const SkRegion& region, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; - apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawRegion(region, p); }); + applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawRegion(region, p); }); } void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); - apply_looper(&paint, [&](const SkPaint& p) { - mCanvas->drawRoundRect(rect, rx, ry, p); - }); + applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawRoundRect(rect, rx, ry, p); }); } void SkiaCanvas::drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner, const Paint& paint) { - apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawDRRect(outer, inner, p); }); + applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawDRRect(outer, inner, p); }); } void SkiaCanvas::drawCircle(float x, float y, float radius, const Paint& paint) { if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return; - apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawCircle(x, y, radius, p); }); + applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawCircle(x, y, radius, p); }); } void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect oval = SkRect::MakeLTRB(left, top, right, bottom); - apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawOval(oval, p); }); + applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawOval(oval, p); }); } void SkiaCanvas::drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, bool useCenter, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect arc = SkRect::MakeLTRB(left, top, right, bottom); - apply_looper(&paint, [&](const SkPaint& p) { + applyLooper(&paint, [&](const SkPaint& p) { if (fabs(sweepAngle) >= 360.0f) { mCanvas->drawOval(arc, p); } else { @@ -537,13 +531,11 @@ void SkiaCanvas::drawPath(const SkPath& path, const Paint& paint) { if (CC_UNLIKELY(path.isEmpty() && (!path.isInverseFillType()))) { return; } - apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawPath(path, p); }); + applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawPath(path, p); }); } void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const Paint& paint) { - apply_looper(&paint, [&](const SkPaint& p) { - mCanvas->drawVertices(vertices, mode, p); - }); + applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawVertices(vertices, mode, p); }); } // ---------------------------------------------------------------------------- @@ -552,7 +544,7 @@ void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, cons void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) { auto image = bitmap.makeImage(); - apply_looper(paint, [&](const SkPaint& p) { + applyLooper(paint, [&](const SkPaint& p) { auto sampling = SkSamplingOptions(p.getFilterQuality()); mCanvas->drawImage(image, left, top, sampling, &p); }); @@ -562,7 +554,7 @@ void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* auto image = bitmap.makeImage(); SkAutoCanvasRestore acr(mCanvas, true); mCanvas->concat(matrix); - apply_looper(paint, [&](const SkPaint& p) { + applyLooper(paint, [&](const SkPaint& p) { auto sampling = SkSamplingOptions(p.getFilterQuality()); mCanvas->drawImage(image, 0, 0, sampling, &p); }); @@ -575,7 +567,7 @@ void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float s SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom); SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); - apply_looper(paint, [&](const SkPaint& p) { + applyLooper(paint, [&](const SkPaint& p) { auto sampling = SkSamplingOptions(p.getFilterQuality()); mCanvas->drawImageRect(image, srcRect, dstRect, sampling, &p, SkCanvas::kFast_SrcRectConstraint); @@ -672,11 +664,11 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, pnt.setShader(image->makeShader(sampling)); auto v = builder.detach(); - apply_looper(&pnt, [&](const SkPaint& p) { + applyLooper(&pnt, [&](const SkPaint& p) { SkPaint copy(p); auto s = SkSamplingOptions(p.getFilterQuality()); if (s != sampling) { - // apply_looper changed the quality? + // applyLooper changed the quality? copy.setShader(image->makeShader(s)); } mCanvas->drawVertices(v, SkBlendMode::kModulate, copy); @@ -707,7 +699,7 @@ void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, floa lattice.fBounds = nullptr; SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); auto image = bitmap.makeImage(); - apply_looper(paint, [&](const SkPaint& p) { + applyLooper(paint, [&](const SkPaint& p) { auto filter = SkSamplingOptions(p.getFilterQuality()).filter; mCanvas->drawImageLattice(image.get(), lattice, dst, filter, &p); }); @@ -746,9 +738,7 @@ void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& pai sk_sp<SkTextBlob> textBlob(builder.make()); - apply_looper(&paintCopy, [&](const SkPaint& p) { - mCanvas->drawTextBlob(textBlob, 0, 0, p); - }); + applyLooper(&paintCopy, [&](const SkPaint& p) { mCanvas->drawTextBlob(textBlob, 0, 0, p); }); drawTextDecorations(x, y, totalAdvance, paintCopy); } @@ -788,9 +778,7 @@ void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, sk_sp<SkTextBlob> textBlob(builder.make()); - apply_looper(&paintCopy, [&](const SkPaint& p) { - mCanvas->drawTextBlob(textBlob, 0, 0, p); - }); + applyLooper(&paintCopy, [&](const SkPaint& p) { mCanvas->drawTextBlob(textBlob, 0, 0, p); }); } // ---------------------------------------------------------------------------- diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index eac3f2217bd8..9ab2b106dbfa 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -169,53 +169,24 @@ protected: const Paint& paint, const SkPath& path, size_t start, size_t end) override; - /** This class acts as a copy on write SkPaint. - * - * Initially this will be the SkPaint passed to the contructor. - * The first time writable() is called this will become a copy of the - * initial SkPaint (or a default SkPaint if nullptr). - */ - struct PaintCoW { - PaintCoW(const SkPaint& that) : mPtr(&that) {} - PaintCoW(const SkPaint* ptr) : mPtr(ptr) {} - PaintCoW(const PaintCoW&) = delete; - PaintCoW(PaintCoW&&) = delete; - PaintCoW& operator=(const PaintCoW&) = delete; - PaintCoW& operator=(PaintCoW&&) = delete; - SkPaint& writeable() { - if (!mStorage) { - if (!mPtr) { - mStorage.emplace(); - } else { - mStorage.emplace(*mPtr); - } - mPtr = &*mStorage; - } - return *mStorage; - } - operator const SkPaint*() const { return mPtr; } - const SkPaint* operator->() const { assert(mPtr); return mPtr; } - explicit operator bool() { return mPtr != nullptr; } - private: - const SkPaint* mPtr; - std::optional<SkPaint> mStorage; - }; + void onFilterPaint(SkPaint& paint); - /** Filters the paint using the current paint filter. - * - * @param paint the paint to filter. Will be initialized with the default - * SkPaint before filtering if filtering is required. - */ - PaintCoW&& filterPaint(PaintCoW&& paint) const; + SkPaint filterPaint(const SkPaint& src) { + SkPaint dst(src); + this->onFilterPaint(dst); + return dst; + } // proc(const SkPaint& modifiedPaint) - template <typename Proc> void apply_looper(const Paint* paint, Proc proc) { - SkPaint skp; - BlurDrawLooper* looper = nullptr; - if (paint) { - skp = *filterPaint(paint); - looper = paint->getLooper(); + template <typename Proc> + void applyLooper(const Paint* paint, Proc proc, void (*preFilter)(SkPaint&) = nullptr) { + BlurDrawLooper* looper = paint ? paint->getLooper() : nullptr; + const SkPaint* skpPtr = paint; + SkPaint skp = skpPtr ? *skpPtr : SkPaint(); + if (preFilter) { + preFilter(skp); } + this->onFilterPaint(skp); if (looper) { looper->apply(skp, [&](SkPoint offset, const SkPaint& modifiedPaint) { mCanvas->save(); @@ -228,7 +199,6 @@ protected: } } - private: struct SaveRec { int saveCount; diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index df66981853bb..f24ba5c1c878 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -240,8 +240,8 @@ static void android_view_ThreadedRenderer_setSdrWhitePoint(JNIEnv* env, jobject static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz, jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) { LOG_ALWAYS_FATAL_IF(frameInfoSize != UI_THREAD_FRAME_INFO_SIZE, - "Mismatched size expectations, given %d expected %d", - frameInfoSize, UI_THREAD_FRAME_INFO_SIZE); + "Mismatched size expectations, given %d expected %zu", frameInfoSize, + UI_THREAD_FRAME_INFO_SIZE); RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); env->GetLongArrayRegion(frameInfo, 0, frameInfoSize, proxy->frameInfo()); return proxy->syncAndDrawFrame(); diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index 04e3a1cb887e..af7271e96cb9 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -170,36 +170,23 @@ void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { // Recording Canvas draw operations: Bitmaps // ---------------------------------------------------------------------------- -SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint) { - bool fixBlending = false; - bool fixAA = false; - if (paint) { - // kClear blend mode is drawn as kDstOut on HW for compatibility with Android O and - // older. - fixBlending = sApiLevel <= 27 && paint->getBlendMode() == SkBlendMode::kClear; - fixAA = paint->isAntiAlias(); +void SkiaRecordingCanvas::FilterForImage(SkPaint& paint) { + // kClear blend mode is drawn as kDstOut on HW for compatibility with Android O and + // older. + if (sApiLevel <= 27 && paint.getBlendMode() == SkBlendMode::kClear) { + paint.setBlendMode(SkBlendMode::kDstOut); } - if (fixBlending || fixAA) { - SkPaint& tmpPaint = paint.writeable(); - - if (fixBlending) { - tmpPaint.setBlendMode(SkBlendMode::kDstOut); - } - - // disabling AA on bitmap draws matches legacy HWUI behavior - tmpPaint.setAntiAlias(false); - } - - return filterPaint(std::move(paint)); + // disabling AA on bitmap draws matches legacy HWUI behavior + paint.setAntiAlias(false); } -static SkFilterMode Paint_to_filter(const SkPaint* paint) { - return paint && paint->getFilterQuality() != kNone_SkFilterQuality ? SkFilterMode::kLinear - : SkFilterMode::kNearest; +static SkFilterMode Paint_to_filter(const SkPaint& paint) { + return paint.getFilterQuality() != kNone_SkFilterQuality ? SkFilterMode::kLinear + : SkFilterMode::kNearest; } -static SkSamplingOptions Paint_to_sampling(const SkPaint* paint) { +static SkSamplingOptions Paint_to_sampling(const SkPaint& paint) { // Android only has 1-bit for "filter", so we don't try to cons-up mipmaps or cubics return SkSamplingOptions(Paint_to_filter(paint), SkMipmapMode::kNone); } @@ -207,9 +194,12 @@ static SkSamplingOptions Paint_to_sampling(const SkPaint* paint) { void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) { sk_sp<SkImage> image = bitmap.makeImage(); - applyLooper(paint, [&](SkScalar x, SkScalar y, const SkPaint* p) { - mRecorder.drawImage(image, left + x, top + y, Paint_to_sampling(p), p, bitmap.palette()); - }); + applyLooper( + paint, + [&](const SkPaint& p) { + mRecorder.drawImage(image, left, top, Paint_to_sampling(p), &p, bitmap.palette()); + }, + FilterForImage); // if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means // it is not safe to store a raw SkImage pointer, because the image object will be destroyed @@ -225,9 +215,12 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, con sk_sp<SkImage> image = bitmap.makeImage(); - applyLooper(paint, [&](SkScalar x, SkScalar y, const SkPaint* p) { - mRecorder.drawImage(image, x, y, Paint_to_sampling(p), p, bitmap.palette()); - }); + applyLooper( + paint, + [&](const SkPaint& p) { + mRecorder.drawImage(image, 0, 0, Paint_to_sampling(p), &p, bitmap.palette()); + }, + FilterForImage); if (!bitmap.isImmutable() && image.get() && !image->unique()) { mDisplayList->mMutableImages.push_back(image.get()); @@ -242,10 +235,13 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop sk_sp<SkImage> image = bitmap.makeImage(); - applyLooper(paint, [&](SkScalar x, SkScalar y, const SkPaint* p) { - mRecorder.drawImageRect(image, srcRect, dstRect.makeOffset(x, y), Paint_to_sampling(p), - p, SkCanvas::kFast_SrcRectConstraint, bitmap.palette()); - }); + applyLooper( + paint, + [&](const SkPaint& p) { + mRecorder.drawImageRect(image, srcRect, dstRect, Paint_to_sampling(p), &p, + SkCanvas::kFast_SrcRectConstraint, bitmap.palette()); + }, + FilterForImage); if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() && !dstRect.isEmpty()) { @@ -281,10 +277,12 @@ void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& ch // HWUI always draws 9-patches with linear filtering, regardless of the Paint. const SkFilterMode filter = SkFilterMode::kLinear; - applyLooper(paint, [&](SkScalar x, SkScalar y, const SkPaint* p) { - mRecorder.drawImageLattice(image, lattice, dst.makeOffset(x, y), filter, p, - bitmap.palette()); - }); + applyLooper( + paint, + [&](const SkPaint& p) { + mRecorder.drawImageLattice(image, lattice, dst, filter, &p, bitmap.palette()); + }, + FilterForImage); if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) { mDisplayList->mMutableImages.push_back(image.get()); diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h index 1e404b845084..ff03e0c5f6d6 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h @@ -87,22 +87,7 @@ private: std::unique_ptr<SkiaDisplayList> mDisplayList; StartReorderBarrierDrawable* mCurrentBarrier; - template <typename Proc> - void applyLooper(const Paint* paint, Proc proc) { - SkPaint skp; - BlurDrawLooper* looper = nullptr; - if (paint) { - skp = *filterBitmap(paint); - looper = paint->getLooper(); - } - if (looper) { - looper->apply(skp, [&](SkPoint offset, const SkPaint& modifiedPaint) { - proc(offset.fX, offset.fY, &modifiedPaint); - }); - } else { - proc(0, 0, &skp); - } - } + static void FilterForImage(SkPaint&); /** * A new SkiaDisplayList is created or recycled if available. @@ -113,7 +98,7 @@ private: */ void initDisplayList(uirenderer::RenderNode* renderNode, int width, int height); - PaintCoW&& filterBitmap(PaintCoW&& paint); + using INHERITED = SkiaCanvas; }; } // namespace skiapipeline diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index b760db287bcb..f69ddacf7ca1 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -484,7 +484,8 @@ void CanvasContext::draw() { // TODO(b/165985262): measure performance impact const auto vsyncId = mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId); if (vsyncId != UiFrameInfoBuilder::INVALID_VSYNC_ID) { - const auto inputEventId = mCurrentFrameInfo->get(FrameInfoIndex::NewestInputEvent); + const auto inputEventId = + static_cast<int32_t>(mCurrentFrameInfo->get(FrameInfoIndex::InputEventId)); native_window_set_frame_timeline_info(mNativeSurface->getNativeWindow(), vsyncId, inputEventId); } @@ -591,7 +592,6 @@ void CanvasContext::draw() { } void CanvasContext::finishFrame(FrameInfo* frameInfo) { - // TODO (b/169858044): Consolidate this into a single call. mJankTracker.finishFrame(*frameInfo); mJankTracker.finishGpuDraw(*frameInfo); diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp index ab23448ab93f..1a3dbe7faaf6 100644 --- a/libs/hwui/tests/unit/TypefaceTests.cpp +++ b/libs/hwui/tests/unit/TypefaceTests.cpp @@ -33,7 +33,7 @@ namespace { constexpr char kRobotoVariable[] = "/system/fonts/Roboto-Regular.ttf"; -constexpr char kRegularFont[] = "/system/fonts/NotoSerif-Regular.ttf"; +constexpr char kRegularFont[] = "/system/fonts/NotoSerif.ttf"; constexpr char kBoldFont[] = "/system/fonts/NotoSerif-Bold.ttf"; constexpr char kItalicFont[] = "/system/fonts/NotoSerif-Italic.ttf"; constexpr char kBoldItalicFont[] = "/system/fonts/NotoSerif-BoldItalic.ttf"; diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index adf58da6a072..5e3966032a11 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -48,13 +48,13 @@ import android.os.ICancellationSignal; */ interface ILocationManager { - @nullable Location getLastLocation(String provider, in LastLocationRequest request, String packageName, String attributionTag); - @nullable ICancellationSignal getCurrentLocation(String provider, in LocationRequest request, in ILocationCallback callback, String packageName, String attributionTag, String listenerId); + @nullable Location getLastLocation(String provider, in LastLocationRequest request, String packageName, @nullable String attributionTag); + @nullable ICancellationSignal getCurrentLocation(String provider, in LocationRequest request, in ILocationCallback callback, String packageName, @nullable String attributionTag, String listenerId); - void registerLocationListener(String provider, in LocationRequest request, in ILocationListener listener, String packageName, String attributionTag, String listenerId); + void registerLocationListener(String provider, in LocationRequest request, in ILocationListener listener, String packageName, @nullable String attributionTag, String listenerId); void unregisterLocationListener(in ILocationListener listener); - void registerLocationPendingIntent(String provider, in LocationRequest request, in PendingIntent pendingIntent, String packageName, String attributionTag); + void registerLocationPendingIntent(String provider, in LocationRequest request, in PendingIntent pendingIntent, String packageName, @nullable String attributionTag); void unregisterLocationPendingIntent(in PendingIntent pendingIntent); void injectLocation(in Location location); @@ -79,24 +79,24 @@ interface ILocationManager @nullable List<GnssAntennaInfo> getGnssAntennaInfos(); - void registerGnssStatusCallback(in IGnssStatusListener callback, String packageName, @nullable String attributionTag); + void registerGnssStatusCallback(in IGnssStatusListener callback, String packageName, @nullable String attributionTag, String listenerId); void unregisterGnssStatusCallback(in IGnssStatusListener callback); - void registerGnssNmeaCallback(in IGnssNmeaListener callback, String packageName, @nullable String attributionTag); + void registerGnssNmeaCallback(in IGnssNmeaListener callback, String packageName, @nullable String attributionTag, String listenerId); void unregisterGnssNmeaCallback(in IGnssNmeaListener callback); - void addGnssMeasurementsListener(in GnssMeasurementRequest request, in IGnssMeasurementsListener listener, String packageName, @nullable String attributionTag); + void addGnssMeasurementsListener(in GnssMeasurementRequest request, in IGnssMeasurementsListener listener, String packageName, @nullable String attributionTag, String listenerId); void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener); void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections); - void addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener, String packageName, @nullable String attributionTag); + void addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener, String packageName, @nullable String attributionTag, String listenerId); void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener); void addProviderRequestListener(in IProviderRequestListener listener); void removeProviderRequestListener(in IProviderRequestListener listener); int getGnssBatchSize(); - void startGnssBatch(long periodNanos, in ILocationListener listener, String packageName, @nullable String attributionTag, @nullable String listenerId); + void startGnssBatch(long periodNanos, in ILocationListener listener, String packageName, @nullable String attributionTag, String listenerId); void flushGnssBatch(); void stopGnssBatch(); diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index b7823400695d..dd5b6e6b4222 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -51,7 +51,7 @@ import android.content.pm.PackageManager; import android.location.provider.IProviderRequestListener; import android.location.provider.ProviderProperties; import android.location.provider.ProviderRequest; -import android.location.provider.ProviderRequest.Listener; +import android.location.provider.ProviderRequest.ChangedListener; import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; @@ -2779,33 +2779,32 @@ public class LocationManager { } /** - * Registers a {@link ProviderRequest.Listener} to all providers. + * Adds a {@link ProviderRequest.ChangedListener} for listening to all providers' + * {@link ProviderRequest} changed events. * * @param executor the executor that the callback runs on * @param listener the listener to register - * @return {@code true} always * @hide */ @SystemApi @RequiresPermission(Manifest.permission.LOCATION_HARDWARE) - public boolean registerProviderRequestListener( + public void addProviderRequestChangedListener( @NonNull @CallbackExecutor Executor executor, - @NonNull Listener listener) { + @NonNull ChangedListener listener) { ProviderRequestLazyLoader.sProviderRequestListeners.addListener(listener, new ProviderRequestTransport(executor, listener)); - return true; } /** - * Unregisters a {@link ProviderRequest.Listener}. + * Removes a {@link ProviderRequest.ChangedListener} that has been added. * * @param listener the listener to remove. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.LOCATION_HARDWARE) - public void unregisterProviderRequestListener( - @NonNull Listener listener) { + public void removeProviderRequestChangedListener( + @NonNull ProviderRequest.ChangedListener listener) { ProviderRequestLazyLoader.sProviderRequestListeners.removeListener(listener); } @@ -2926,7 +2925,8 @@ public class LocationManager { protected void registerTransport(GnssStatusTransport transport) throws RemoteException { getService().registerGnssStatusCallback(transport, transport.getPackage(), - transport.getAttributionTag()); + transport.getAttributionTag(), + AppOpsManager.toReceiverId(transport.getListener())); } @Override @@ -2947,7 +2947,8 @@ public class LocationManager { protected void registerTransport(GnssNmeaTransport transport) throws RemoteException { getService().registerGnssNmeaCallback(transport, transport.getPackage(), - transport.getAttributionTag()); + transport.getAttributionTag(), + AppOpsManager.toReceiverId(transport.getListener())); } @Override @@ -2968,7 +2969,8 @@ public class LocationManager { protected void registerTransport(GnssMeasurementsTransport transport) throws RemoteException { getService().addGnssMeasurementsListener(transport.getRequest(), transport, - transport.getPackage(), transport.getAttributionTag()); + transport.getPackage(), transport.getAttributionTag(), + AppOpsManager.toReceiverId(transport.getListener())); } @Override @@ -3008,7 +3010,8 @@ public class LocationManager { protected void registerTransport(GnssNavigationTransport transport) throws RemoteException { getService().addGnssNavigationMessageListener(transport, - transport.getPackage(), transport.getAttributionTag()); + transport.getPackage(), transport.getAttributionTag(), + AppOpsManager.toReceiverId(transport.getListener())); } @Override @@ -3442,13 +3445,13 @@ public class LocationManager { } private static class ProviderRequestTransport extends IProviderRequestListener.Stub - implements ListenerTransport<ProviderRequest.Listener> { + implements ListenerTransport<ChangedListener> { private final Executor mExecutor; - private volatile @Nullable ProviderRequest.Listener mListener; + private volatile @Nullable ProviderRequest.ChangedListener mListener; - ProviderRequestTransport(Executor executor, ProviderRequest.Listener listener) { + ProviderRequestTransport(Executor executor, ChangedListener listener) { Preconditions.checkArgument(executor != null, "invalid null executor"); Preconditions.checkArgument(listener != null, "invalid null callback"); mExecutor = executor; @@ -3461,7 +3464,7 @@ public class LocationManager { } @Override - public @Nullable ProviderRequest.Listener getListener() { + public @Nullable ProviderRequest.ChangedListener getListener() { return mListener; } diff --git a/location/java/android/location/provider/ProviderRequest.java b/location/java/android/location/provider/ProviderRequest.java index b6ec32309b08..b72d36519e72 100644 --- a/location/java/android/location/provider/ProviderRequest.java +++ b/location/java/android/location/provider/ProviderRequest.java @@ -56,10 +56,13 @@ public final class ProviderRequest implements Parcelable { /** * Listener to be invoked when a new request is set to the provider. */ - public interface Listener { + public interface ChangedListener { /** * Invoked when a new request is set. + * + * @param provider the location provider associated with the request + * @param request the new {@link ProviderRequest} */ void onProviderRequestChanged(@NonNull String provider, @NonNull ProviderRequest request); } diff --git a/location/java/android/location/util/identity/CallerIdentity.java b/location/java/android/location/util/identity/CallerIdentity.java index 0bb7dbb71ae8..85a083ee84bc 100644 --- a/location/java/android/location/util/identity/CallerIdentity.java +++ b/location/java/android/location/util/identity/CallerIdentity.java @@ -42,7 +42,16 @@ public final class CallerIdentity { @VisibleForTesting public static CallerIdentity forTest(int uid, int pid, String packageName, @Nullable String attributionTag) { - return new CallerIdentity(uid, pid, packageName, attributionTag, null); + return forTest(uid, pid, packageName, attributionTag, null); + } + + /** + * Construct a CallerIdentity for test purposes. + */ + @VisibleForTesting + public static CallerIdentity forTest(int uid, int pid, String packageName, + @Nullable String attributionTag, @Nullable String listenerId) { + return new CallerIdentity(uid, pid, packageName, attributionTag, listenerId); } /** @@ -145,7 +154,10 @@ public final class CallerIdentity { return mAttributionTag; } - /** The calling listener id. */ + /** + * The calling listener id. A null listener id will match any other listener id for the purposes + * of {@link #equals(Object)}. + */ public String getListenerId() { return mListenerId; } @@ -168,6 +180,17 @@ public final class CallerIdentity { } } + /** + * Returns a CallerIdentity corrosponding to this CallerIdentity but with a null listener id. + */ + public CallerIdentity stripListenerId() { + if (mListenerId == null) { + return this; + } else { + return new CallerIdentity(mUid, mPid, mPackageName, mAttributionTag, null); + } + } + @Override public String toString() { int length = 10 + mPackageName.length(); @@ -201,15 +224,12 @@ public final class CallerIdentity { return false; } CallerIdentity that = (CallerIdentity) o; - return equalsIgnoringListenerId(that) && Objects.equals(mListenerId, that.mListenerId); - } - - public boolean equalsIgnoringListenerId(CallerIdentity that) { - return that != null - && mUid == that.mUid + return mUid == that.mUid && mPid == that.mPid && mPackageName.equals(that.mPackageName) - && Objects.equals(mAttributionTag, that.mAttributionTag); + && Objects.equals(mAttributionTag, that.mAttributionTag) + && (mListenerId == null || that.mListenerId == null || mListenerId.equals( + that.mListenerId)); } @Override diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java index 44f8385b715e..9ab4aac891e5 100644 --- a/media/java/android/media/ImageWriter.java +++ b/media/java/android/media/ImageWriter.java @@ -131,7 +131,59 @@ public class ImageWriter implements AutoCloseable { */ public static @NonNull ImageWriter newInstance(@NonNull Surface surface, @IntRange(from = 1) int maxImages) { - return new ImageWriter(surface, maxImages, ImageFormat.UNKNOWN); + return new ImageWriter(surface, maxImages, ImageFormat.UNKNOWN, -1 /*width*/, + -1 /*height*/); + } + + /** + * <p> + * Create a new ImageWriter with given number of max Images, format and producer dimension. + * </p> + * <p> + * The {@code maxImages} parameter determines the maximum number of + * {@link Image} objects that can be be dequeued from the + * {@code ImageWriter} simultaneously. Requesting more buffers will use up + * more memory, so it is important to use only the minimum number necessary. + * </p> + * <p> + * The format specifies the image format of this ImageWriter. The format + * from the {@code surface} will be overridden with this format. For example, + * if the surface is obtained from a {@link android.graphics.SurfaceTexture}, the default + * format may be {@link PixelFormat#RGBA_8888}. If the application creates an ImageWriter + * with this surface and {@link ImageFormat#PRIVATE}, this ImageWriter will be able to operate + * with {@link ImageFormat#PRIVATE} Images. + * </p> + * <p> + * Note that the consumer end-point may or may not be able to support Images with different + * format, for such case, the application should only use this method if the consumer is able + * to consume such images. + * </p> + * <p> The input Image size can also be set by the client. </p> + * + * @param surface The destination Surface this writer produces Image data + * into. + * @param maxImages The maximum number of Images the user will want to + * access simultaneously for producing Image data. This should be + * as small as possible to limit memory use. Once maxImages + * Images are dequeued by the user, one of them has to be queued + * back before a new Image can be dequeued for access via + * {@link #dequeueInputImage()}. + * @param format The format of this ImageWriter. It can be any valid format specified by + * {@link ImageFormat} or {@link PixelFormat}. + * + * @param width Input size width. + * @param height Input size height. + * + * @return a new ImageWriter instance. + * + * @hide + */ + public static @NonNull ImageWriter newInstance(@NonNull Surface surface, + @IntRange(from = 1) int maxImages, @Format int format, int width, int height) { + if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) { + throw new IllegalArgumentException("Invalid format is specified: " + format); + } + return new ImageWriter(surface, maxImages, format, width, height); } /** @@ -180,13 +232,13 @@ public class ImageWriter implements AutoCloseable { if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) { throw new IllegalArgumentException("Invalid format is specified: " + format); } - return new ImageWriter(surface, maxImages, format); + return new ImageWriter(surface, maxImages, format, -1 /*width*/, -1 /*height*/); } /** * @hide */ - protected ImageWriter(Surface surface, int maxImages, int format) { + protected ImageWriter(Surface surface, int maxImages, int format, int width, int height) { if (surface == null || maxImages < 1) { throw new IllegalArgumentException("Illegal input argument: surface " + surface + ", maxImages: " + maxImages); @@ -196,7 +248,8 @@ public class ImageWriter implements AutoCloseable { // Note that the underlying BufferQueue is working in synchronous mode // to avoid dropping any buffers. - mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, format); + mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, format, width, + height); // nativeInit internally overrides UNKNOWN format. So does surface format query after // nativeInit and before getEstimatedNativeAllocBytes(). @@ -919,7 +972,7 @@ public class ImageWriter implements AutoCloseable { // Native implemented ImageWriter methods. private synchronized native long nativeInit(Object weakSelf, Surface surface, int maxImgs, - int format); + int format, int width, int height); private synchronized native void nativeClose(long nativeCtx); diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index c51c9dd06c24..f3cee17ab238 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -19,6 +19,7 @@ package android.media; import static android.Manifest.permission.BIND_IMS_SERVICE; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -91,6 +92,7 @@ import java.util.Scanner; import java.util.Set; import java.util.UUID; import java.util.Vector; +import java.util.concurrent.Executor; /** @@ -2172,7 +2174,7 @@ public class MediaPlayer extends PlayerBase mOnVideoSizeChangedListener = null; mOnTimedTextListener = null; mOnRtpRxNoticeListener = null; - mOnRtpRxNoticeHandler = null; + mOnRtpRxNoticeExecutor = null; synchronized (mTimeProviderLock) { if (mTimeProvider != null) { mTimeProvider.close(); @@ -3711,7 +3713,6 @@ public class MediaPlayer extends PlayerBase case MEDIA_RTP_RX_NOTICE: final OnRtpRxNoticeListener rtpRxNoticeListener = mOnRtpRxNoticeListener; - final Handler rtpRxNoticeHandler = mOnRtpRxNoticeHandler; if (rtpRxNoticeListener == null) { return; } @@ -3730,14 +3731,9 @@ public class MediaPlayer extends PlayerBase } finally { parcel.recycle(); } - if (rtpRxNoticeHandler == null) { - rtpRxNoticeListener.onRtpRxNotice(mMediaPlayer, noticeType, data); - } else { - rtpRxNoticeHandler.post( - () -> - rtpRxNoticeListener - .onRtpRxNotice(mMediaPlayer, noticeType, data)); - } + mOnRtpRxNoticeExecutor.execute(() -> + rtpRxNoticeListener + .onRtpRxNotice(mMediaPlayer, noticeType, data)); } return; @@ -4305,28 +4301,26 @@ public class MediaPlayer extends PlayerBase * * @see OnRtpRxNoticeListener * - * @param listener the listener called after a notice from RTP Rx - * @param handler the {@link Handler} that receives RTP Tx events. If null is passed, - * notifications will be posted on the thread that created this MediaPlayer - * instance. If the creating thread does not have a {@link Looper}, then - * notifications will be posted on the main thread. + * @param listener the listener called after a notice from RTP Rx. + * @param executor the {@link Executor} on which to post RTP Tx events. * @hide */ @SystemApi @RequiresPermission(BIND_IMS_SERVICE) public void setOnRtpRxNoticeListener( @NonNull Context context, - @NonNull OnRtpRxNoticeListener listener, @Nullable Handler handler) { + @NonNull @CallbackExecutor Executor executor, + @NonNull OnRtpRxNoticeListener listener) { Objects.requireNonNull(context); Preconditions.checkArgument( context.checkSelfPermission(BIND_IMS_SERVICE) == PERMISSION_GRANTED, BIND_IMS_SERVICE + " permission not granted."); mOnRtpRxNoticeListener = Objects.requireNonNull(listener); - mOnRtpRxNoticeHandler = handler; + mOnRtpRxNoticeExecutor = Objects.requireNonNull(executor); } private OnRtpRxNoticeListener mOnRtpRxNoticeListener; - private Handler mOnRtpRxNoticeHandler; + private Executor mOnRtpRxNoticeExecutor; /** * Register a callback to be invoked when a selected track has timed metadata available. diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 8de5e42e93b2..aa0f7fdd70d5 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -34,6 +34,7 @@ import android.media.Session2Token; import android.media.VolumeProvider; import android.os.Bundle; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.UserHandle; @@ -321,7 +322,7 @@ public final class MediaSessionManager { @NonNull OnActiveSessionsChangedListener sessionListener, @Nullable ComponentName notificationListener, @Nullable Handler handler) { addOnActiveSessionsChangedListener(sessionListener, notificationListener, - UserHandle.myUserId(), handler); + UserHandle.myUserId(), handler == null ? null : new HandlerExecutor(handler)); } /** @@ -337,38 +338,40 @@ public final class MediaSessionManager { * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission in order to * add listeners for user ids that do not belong to current process. * - * @param sessionListener The listener to add. * @param notificationListener The enabled notification listener component. May be null. * @param userHandle The user handle to listen for changes on. - * @param handler The handler to post updates on. + * @param executor The executor on which the listener should be invoked + * @param sessionListener The listener to add. * @hide */ - @SuppressLint({"ExecutorRegistration", "SamShouldBeLast", "UserHandle"}) + @SuppressLint("UserHandle") @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public void addOnActiveSessionsChangedListener( - @NonNull OnActiveSessionsChangedListener sessionListener, - @Nullable ComponentName notificationListener, @NonNull UserHandle userHandle, - @Nullable Handler handler) { + @Nullable ComponentName notificationListener, + @NonNull UserHandle userHandle, @NonNull Executor executor, + @NonNull OnActiveSessionsChangedListener sessionListener) { Objects.requireNonNull(userHandle, "userHandle shouldn't be null"); + Objects.requireNonNull(executor, "executor shouldn't be null"); addOnActiveSessionsChangedListener(sessionListener, notificationListener, - userHandle.getIdentifier(), handler); + userHandle.getIdentifier(), executor); } private void addOnActiveSessionsChangedListener( @NonNull OnActiveSessionsChangedListener sessionListener, @Nullable ComponentName notificationListener, int userId, - @Nullable Handler handler) { + @Nullable Executor executor) { Objects.requireNonNull(sessionListener, "sessionListener shouldn't be null"); - if (handler == null) { - handler = new Handler(); + if (executor == null) { + executor = new HandlerExecutor(new Handler()); } + synchronized (mLock) { if (mListeners.get(sessionListener) != null) { Log.w(TAG, "Attempted to add session listener twice, ignoring."); return; } SessionsChangedWrapper wrapper = new SessionsChangedWrapper(mContext, sessionListener, - handler); + executor); try { mService.addSessionsListener(wrapper.mStub, notificationListener, userId); mListeners.put(sessionListener, wrapper); @@ -412,7 +415,8 @@ public final class MediaSessionManager { */ public void addOnSession2TokensChangedListener( @NonNull OnSession2TokensChangedListener listener) { - addOnSession2TokensChangedListener(UserHandle.myUserId(), listener, new Handler()); + addOnSession2TokensChangedListener(UserHandle.myUserId(), listener, + new HandlerExecutor(new Handler())); } /** @@ -428,7 +432,9 @@ public final class MediaSessionManager { */ public void addOnSession2TokensChangedListener( @NonNull OnSession2TokensChangedListener listener, @NonNull Handler handler) { - addOnSession2TokensChangedListener(UserHandle.myUserId(), listener, handler); + Objects.requireNonNull(handler, "handler shouldn't be null"); + addOnSession2TokensChangedListener(UserHandle.myUserId(), listener, + new HandlerExecutor(handler)); } /** @@ -445,20 +451,19 @@ public final class MediaSessionManager { * * @param userHandle The userHandle to listen for changes on * @param listener The listener to add - * @param handler The handler to call listener on. If {@code null}, calling thread's looper will - * be used. + * @param executor The executor on which the listener should be invoked * @hide */ @SuppressLint("UserHandle") public void addOnSession2TokensChangedListener(@NonNull UserHandle userHandle, - @NonNull OnSession2TokensChangedListener listener, @NonNull Handler handler) { + @NonNull OnSession2TokensChangedListener listener, @NonNull Executor executor) { Objects.requireNonNull(userHandle, "userHandle shouldn't be null"); - addOnSession2TokensChangedListener(userHandle.getIdentifier(), listener, handler); + Objects.requireNonNull(executor, "executor shouldn't be null"); + addOnSession2TokensChangedListener(userHandle.getIdentifier(), listener, executor); } private void addOnSession2TokensChangedListener(int userId, - OnSession2TokensChangedListener listener, Handler handler) { - Objects.requireNonNull(handler, "handler shouldn't be null"); + OnSession2TokensChangedListener listener, Executor executor) { Objects.requireNonNull(listener, "listener shouldn't be null"); synchronized (mLock) { if (mSession2TokensListeners.get(listener) != null) { @@ -466,7 +471,7 @@ public final class MediaSessionManager { return; } Session2TokensChangedWrapper wrapper = - new Session2TokensChangedWrapper(listener, handler); + new Session2TokensChangedWrapper(listener, executor); try { mService.addSession2TokensListener(wrapper.getStub(), userId); mSession2TokensListeners.put(listener, wrapper); @@ -847,7 +852,7 @@ public final class MediaSessionManager { /** * Add a {@link OnMediaKeyEventDispatchedListener}. * - * @param executor The executor on which the callback should be invoked + * @param executor The executor on which the listener should be invoked * @param listener A {@link OnMediaKeyEventDispatchedListener}. * @hide */ @@ -898,7 +903,7 @@ public final class MediaSessionManager { /** * Add a {@link OnMediaKeyEventDispatchedListener}. * - * @param executor The executor on which the callback should be invoked + * @param executor The executor on which the listener should be invoked * @param listener A {@link OnMediaKeyEventSessionChangedListener}. * @hide */ @@ -1257,62 +1262,61 @@ public final class MediaSessionManager { private static final class SessionsChangedWrapper { private Context mContext; private OnActiveSessionsChangedListener mListener; - private Handler mHandler; + private Executor mExecutor; public SessionsChangedWrapper(Context context, OnActiveSessionsChangedListener listener, - Handler handler) { + Executor executor) { mContext = context; mListener = listener; - mHandler = handler; + mExecutor = executor; } private final IActiveSessionsListener.Stub mStub = new IActiveSessionsListener.Stub() { @Override public void onActiveSessionsChanged(final List<MediaSession.Token> tokens) { - final Handler handler = mHandler; - if (handler != null) { - handler.post(new Runnable() { - @Override - public void run() { - final Context context = mContext; - if (context != null) { - ArrayList<MediaController> controllers = new ArrayList<>(); - int size = tokens.size(); - for (int i = 0; i < size; i++) { - controllers.add(new MediaController(context, tokens.get(i))); - } - final OnActiveSessionsChangedListener listener = mListener; - if (listener != null) { - listener.onActiveSessionsChanged(controllers); - } - } - } - }); + if (mExecutor != null) { + final Executor executor = mExecutor; + executor.execute(() -> callOnActiveSessionsChangedListener(tokens)); } } }; + private void callOnActiveSessionsChangedListener(final List<MediaSession.Token> tokens) { + final Context context = mContext; + if (context != null) { + ArrayList<MediaController> controllers = new ArrayList<>(); + int size = tokens.size(); + for (int i = 0; i < size; i++) { + controllers.add(new MediaController(context, tokens.get(i))); + } + final OnActiveSessionsChangedListener listener = mListener; + if (listener != null) { + listener.onActiveSessionsChanged(controllers); + } + } + } + private void release() { mListener = null; mContext = null; - mHandler = null; + mExecutor = null; } } private static final class Session2TokensChangedWrapper { private final OnSession2TokensChangedListener mListener; - private final Handler mHandler; + private final Executor mExecutor; private final ISession2TokensListener.Stub mStub = new ISession2TokensListener.Stub() { @Override public void onSession2TokensChanged(final List<Session2Token> tokens) { - mHandler.post(() -> mListener.onSession2TokensChanged(tokens)); + mExecutor.execute(() -> mListener.onSession2TokensChanged(tokens)); } }; - Session2TokensChangedWrapper(OnSession2TokensChangedListener listener, Handler handler) { + Session2TokensChangedWrapper(OnSession2TokensChangedListener listener, Executor executor) { mListener = listener; - mHandler = (handler == null) ? new Handler() : new Handler(handler.getLooper()); + mExecutor = executor; } public ISession2TokensListener.Stub getStub() { diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp index 5d959a3930f4..b291ac95bf4f 100644 --- a/media/jni/android_media_ImageWriter.cpp +++ b/media/jni/android_media_ImageWriter.cpp @@ -364,7 +364,7 @@ static void ImageWriter_classInit(JNIEnv* env, jclass clazz) { } static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobject jsurface, - jint maxImages, jint userFormat) { + jint maxImages, jint userFormat, jint userWidth, jint userHeight) { status_t res; ALOGV("%s: maxImages:%d", __FUNCTION__, maxImages); @@ -405,20 +405,38 @@ static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobje // Get the dimension and format of the producer. sp<ANativeWindow> anw = producer; int32_t width, height, surfaceFormat; - if ((res = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, &width)) != OK) { - ALOGE("%s: Query Surface width failed: %s (%d)", __FUNCTION__, strerror(-res), res); - jniThrowRuntimeException(env, "Failed to query Surface width"); - return 0; + if (userWidth < 0) { + if ((res = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, &width)) != OK) { + ALOGE("%s: Query Surface width failed: %s (%d)", __FUNCTION__, strerror(-res), res); + jniThrowRuntimeException(env, "Failed to query Surface width"); + return 0; + } + } else { + width = userWidth; } + ctx->setBufferWidth(width); - if ((res = anw->query(anw.get(), NATIVE_WINDOW_HEIGHT, &height)) != OK) { - ALOGE("%s: Query Surface height failed: %s (%d)", __FUNCTION__, strerror(-res), res); - jniThrowRuntimeException(env, "Failed to query Surface height"); - return 0; + if (userHeight < 0) { + if ((res = anw->query(anw.get(), NATIVE_WINDOW_HEIGHT, &height)) != OK) { + ALOGE("%s: Query Surface height failed: %s (%d)", __FUNCTION__, strerror(-res), res); + jniThrowRuntimeException(env, "Failed to query Surface height"); + return 0; + } + } else { + height = userHeight; } ctx->setBufferHeight(height); + if ((userWidth > 0) && (userHeight > 0)) { + res = native_window_set_buffers_user_dimensions(anw.get(), userWidth, userHeight); + if (res != OK) { + ALOGE("%s: Set buffer dimensions failed: %s (%d)", __FUNCTION__, strerror(-res), res); + jniThrowRuntimeException(env, "Set buffer dimensions failed"); + return 0; + } + } + // Query surface format if no valid user format is specified, otherwise, override surface format // with user format. if (userFormat == IMAGE_FORMAT_UNKNOWN) { @@ -1045,7 +1063,7 @@ static jobjectArray Image_createSurfacePlanes(JNIEnv* env, jobject thiz, static JNINativeMethod gImageWriterMethods[] = { {"nativeClassInit", "()V", (void*)ImageWriter_classInit }, - {"nativeInit", "(Ljava/lang/Object;Landroid/view/Surface;II)J", + {"nativeInit", "(Ljava/lang/Object;Landroid/view/Surface;IIII)J", (void*)ImageWriter_init }, {"nativeClose", "(J)V", (void*)ImageWriter_close }, {"nativeAttachAndQueueImage", "(JJIJIIIIII)I", (void*)ImageWriter_attachAndQueueImage }, diff --git a/media/jni/android_media_JetPlayer.cpp b/media/jni/android_media_JetPlayer.cpp index 481f80b278f8..10a5b586c2b9 100644 --- a/media/jni/android_media_JetPlayer.cpp +++ b/media/jni/android_media_JetPlayer.cpp @@ -43,12 +43,34 @@ struct fields_t { jfieldID nativePlayerInJavaObj; // stores in Java the native JetPlayer object }; -static fields_t javaJetPlayerFields; +static fields_t javaJetPlayerFields {}; +#define JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME "mNativePlayerInJavaObj" +#define JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME "postEventFromNative" + +static void initializeJavaIDs(JNIEnv* env) { + static std::once_flag sJniInitialized; + + std::call_once(sJniInitialized, [](JNIEnv* env) { + // Get the JetPlayer java class + jclass jetPlayerClass = FindClassOrDie(env, kClassPathName); + javaJetPlayerFields.jetClass = MakeGlobalRefOrDie(env, jetPlayerClass); + + // Get the mNativePlayerInJavaObj variable field + javaJetPlayerFields.nativePlayerInJavaObj = + GetFieldIDOrDie(env, jetPlayerClass, JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME, "J"); + + // Get the callback to post events from this native code to Java + javaJetPlayerFields.postNativeEventInJava = GetStaticMethodIDOrDie(env, + javaJetPlayerFields.jetClass, JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME, + "(Ljava/lang/Object;III)V"); + }, env); +} // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- + /* * This function is called from JetPlayer instance's render thread */ @@ -79,6 +101,8 @@ static jboolean android_media_JetPlayer_setup(JNIEnv *env, jobject thiz, jobject weak_this, jint maxTracks, jint trackBufferSize) { + initializeJavaIDs(env); + //ALOGV("android_media_JetPlayer_setup(): entering."); JetPlayer* lpJet = new JetPlayer(env->NewGlobalRef(weak_this), maxTracks, trackBufferSize); @@ -511,28 +535,9 @@ static const JNINativeMethod gMethods[] = { {"native_clearQueue", "()Z", (void *)android_media_JetPlayer_clearQueue}, }; -#define JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME "mNativePlayerInJavaObj" -#define JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME "postEventFromNative" int register_android_media_JetPlayer(JNIEnv *env) { - javaJetPlayerFields.jetClass = NULL; - javaJetPlayerFields.postNativeEventInJava = NULL; - javaJetPlayerFields.nativePlayerInJavaObj = NULL; - - // Get the JetPlayer java class - jclass jetPlayerClass = FindClassOrDie(env, kClassPathName); - javaJetPlayerFields.jetClass = MakeGlobalRefOrDie(env, jetPlayerClass); - - // Get the mNativePlayerInJavaObj variable field - javaJetPlayerFields.nativePlayerInJavaObj = GetFieldIDOrDie(env, - jetPlayerClass, JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME, "J"); - - // Get the callback to post events from this native code to Java - javaJetPlayerFields.postNativeEventInJava = GetStaticMethodIDOrDie(env, - javaJetPlayerFields.jetClass, JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME, - "(Ljava/lang/Object;III)V"); - return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); } diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index 71c86cc7d42d..1870a939f0dc 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -61,6 +61,7 @@ #include <media/stagefright/foundation/AString.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/PersistentSurface.h> +#include <mediadrm/DrmUtils.h> #include <mediadrm/ICrypto.h> #include <private/android/AHardwareBufferHelpers.h> @@ -312,6 +313,7 @@ status_t JMediaCodec::configure( mGraphicOutput = (mime.startsWithIgnoreCase("video/") || mime.startsWithIgnoreCase("image/")) && !(flags & CONFIGURE_FLAG_ENCODE); mHasCryptoOrDescrambler = (crypto != nullptr) || (descrambler != nullptr); + mCrypto = crypto; return mCodec->configure( format, mSurfaceTextureClient, crypto, descrambler, flags); @@ -1103,6 +1105,8 @@ void JMediaCodec::onMessageReceived(const sp<AMessage> &msg) { } } +jint MediaErrorToJavaError(status_t err); + } // namespace android //////////////////////////////////////////////////////////////////////////////// @@ -1150,7 +1154,8 @@ static void throwCodecException(JNIEnv *env, status_t err, int32_t actionCode, c env->Throw(exception); } -static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) { +static void throwCryptoException(JNIEnv *env, status_t err, const char *msg, + const sp<ICrypto> &crypto) { ScopedLocalRef<jclass> clazz( env, env->FindClass("android/media/MediaCodec$CryptoException")); CHECK(clazz.get() != NULL); @@ -1159,7 +1164,7 @@ static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) { env->GetMethodID(clazz.get(), "<init>", "(ILjava/lang/String;)V"); CHECK(constructID != NULL); - const char *defaultMsg = "Unknown Error"; + std::string defaultMsg = "Unknown Error"; /* translate OS errors to Java API CryptoException errorCodes (which are positive) */ switch (err) { @@ -1199,11 +1204,17 @@ static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) { err = gCryptoErrorCodes.cryptoErrorLostState; defaultMsg = "Session state was lost, open a new session and retry"; break; - default: /* Other negative DRM error codes go out as is. */ + default: /* Other negative DRM error codes go out best-effort. */ + err = MediaErrorToJavaError(err); + defaultMsg = StrCryptoError(err); break; } - jstring msgObj = env->NewStringUTF(msg != NULL ? msg : defaultMsg); + std::string msgStr(msg != NULL ? msg : defaultMsg.c_str()); + if (crypto != NULL) { + msgStr = DrmUtils::GetExceptionMessage(err, msgStr.c_str(), crypto); + } + jstring msgObj = env->NewStringUTF(msgStr.c_str()); jthrowable exception = (jthrowable)env->NewObject(clazz.get(), constructID, err, msgObj); @@ -1213,7 +1224,7 @@ static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) { static jint throwExceptionAsNecessary( JNIEnv *env, status_t err, int32_t actionCode = ACTION_CODE_FATAL, - const char *msg = NULL) { + const char *msg = NULL, const sp<ICrypto>& crypto = NULL) { switch (err) { case OK: return 0; @@ -1237,7 +1248,7 @@ static jint throwExceptionAsNecessary( default: if (isCryptoError(err)) { - throwCryptoException(env, err, msg); + throwCryptoException(env, err, msg, crypto); return 0; } throwCodecException(env, err, actionCode, msg); @@ -1899,7 +1910,8 @@ static void android_media_MediaCodec_queueSecureInputBuffer( subSamples = NULL; throwExceptionAsNecessary( - env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str()); + env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str(), + codec->getCrypto()); } static jobject android_media_MediaCodec_mapHardwareBuffer(JNIEnv *env, jclass, jobject bufferObj) { diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h index a58f9a74b563..f16bcf3c88e4 100644 --- a/media/jni/android_media_MediaCodec.h +++ b/media/jni/android_media_MediaCodec.h @@ -164,6 +164,8 @@ struct JMediaCodec : public AHandler { bool hasCryptoOrDescrambler() { return mHasCryptoOrDescrambler; } + const sp<ICrypto> &getCrypto() { return mCrypto; } + protected: virtual ~JMediaCodec(); @@ -193,6 +195,8 @@ private: status_t mInitStatus; + sp<ICrypto> mCrypto; + template <typename T> status_t createByteBufferFromABuffer( JNIEnv *env, bool readOnly, bool clearBuffer, const sp<T> &buffer, diff --git a/media/jni/android_media_MediaCrypto.cpp b/media/jni/android_media_MediaCrypto.cpp index 517672ee6127..f491be884b2f 100644 --- a/media/jni/android_media_MediaCrypto.cpp +++ b/media/jni/android_media_MediaCrypto.cpp @@ -202,10 +202,11 @@ static void android_media_MediaCrypto_native_setup( uuid = NULL; if (err != OK) { - jniThrowException( + std::string strerr(StrCryptoError(err)); + jniThrowExceptionFmt( env, "android/media/MediaCryptoException", - "Failed to instantiate crypto object."); + "Failed to instantiate crypto object: %s", strerr.c_str()); return; } @@ -295,7 +296,8 @@ static void android_media_MediaCrypto_setMediaDrmSession( } else if (err == NO_INIT) { msg += ": crypto plugin not initialized"; } else { - msg.appendFormat(": general failure (%d)", err); + std::string strerr(StrCryptoError(err)); + msg.appendFormat(": general failure (%s)", strerr.c_str()); } jniThrowException(env, "android/media/MediaCryptoException", msg.string()); } diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index eee9f1e08131..3669bf44c42a 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -217,23 +217,34 @@ namespace android { void LnbClientCallbackImpl::onEvent(const LnbEventType lnbEventType) { ALOGD("LnbClientCallbackImpl::onEvent, type=%d", lnbEventType); JNIEnv *env = AndroidRuntime::getJNIEnv(); - env->CallVoidMethod( - mLnbObj, - gFields.onLnbEventID, - (jint)lnbEventType); + jobject lnb(env->NewLocalRef(mLnbObj)); + if (!env->IsSameObject(lnb, nullptr)) { + env->CallVoidMethod( + lnb, + gFields.onLnbEventID, + (jint)lnbEventType); + } else { + ALOGE("LnbClientCallbackImpl::onEvent:" + "Lnb object has been freed. Ignoring callback."); + } } void LnbClientCallbackImpl::onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) { ALOGD("LnbClientCallbackImpl::onDiseqcMessage"); JNIEnv *env = AndroidRuntime::getJNIEnv(); - jbyteArray array = env->NewByteArray(diseqcMessage.size()); - env->SetByteArrayRegion( - array, 0, diseqcMessage.size(), reinterpret_cast<jbyte*>(diseqcMessage[0])); - - env->CallVoidMethod( - mLnbObj, - gFields.onLnbDiseqcMessageID, - array); + jobject lnb(env->NewLocalRef(mLnbObj)); + if (!env->IsSameObject(lnb, nullptr)) { + jbyteArray array = env->NewByteArray(diseqcMessage.size()); + env->SetByteArrayRegion( + array, 0, diseqcMessage.size(), reinterpret_cast<jbyte*>(diseqcMessage[0])); + env->CallVoidMethod( + lnb, + gFields.onLnbDiseqcMessageID, + array); + } else { + ALOGE("LnbClientCallbackImpl::onDiseqcMessage:" + "Lnb object has been freed. Ignoring callback."); + } } void LnbClientCallbackImpl::setLnb(jweak lnbObj) { @@ -254,19 +265,31 @@ LnbClientCallbackImpl::~LnbClientCallbackImpl() { void DvrClientCallbackImpl::onRecordStatus(RecordStatus status) { ALOGD("DvrClientCallbackImpl::onRecordStatus"); JNIEnv *env = AndroidRuntime::getJNIEnv(); - env->CallVoidMethod( - mDvrObj, - gFields.onDvrRecordStatusID, - (jint) status); + jobject dvr(env->NewLocalRef(mDvrObj)); + if (!env->IsSameObject(dvr, nullptr)) { + env->CallVoidMethod( + dvr, + gFields.onDvrRecordStatusID, + (jint) status); + } else { + ALOGE("DvrClientCallbackImpl::onRecordStatus:" + "Dvr object has been freed. Ignoring callback."); + } } void DvrClientCallbackImpl::onPlaybackStatus(PlaybackStatus status) { ALOGD("DvrClientCallbackImpl::onPlaybackStatus"); JNIEnv *env = AndroidRuntime::getJNIEnv(); - env->CallVoidMethod( - mDvrObj, - gFields.onDvrPlaybackStatusID, - (jint) status); + jobject dvr(env->NewLocalRef(mDvrObj)); + if (!env->IsSameObject(dvr, nullptr)) { + env->CallVoidMethod( + dvr, + gFields.onDvrPlaybackStatusID, + (jint) status); + } else { + ALOGE("DvrClientCallbackImpl::onPlaybackStatus:" + "Dvr object has been freed. Ignoring callback."); + } } void DvrClientCallbackImpl::setDvr(jweak dvrObj) { @@ -810,10 +833,16 @@ void FilterClientCallbackImpl::onFilterEvent_1_1(const DemuxFilterEvent& filterE } } } - env->CallVoidMethod( - mFilterObj, - gFields.onFilterEventID, - array); + jobject filter(env->NewLocalRef(mFilterObj)); + if (!env->IsSameObject(filter, nullptr)) { + env->CallVoidMethod( + filter, + gFields.onFilterEventID, + array); + } else { + ALOGE("FilterClientCallbackImpl::onFilterEvent_1_1:" + "Filter object has been freed. Ignoring callback."); + } } void FilterClientCallbackImpl::onFilterEvent(const DemuxFilterEvent& filterEvent) { @@ -828,10 +857,16 @@ void FilterClientCallbackImpl::onFilterEvent(const DemuxFilterEvent& filterEvent void FilterClientCallbackImpl::onFilterStatus(const DemuxFilterStatus status) { ALOGD("FilterClientCallbackImpl::onFilterStatus"); JNIEnv *env = AndroidRuntime::getJNIEnv(); - env->CallVoidMethod( - mFilterObj, - gFields.onFilterStatusID, - (jint)status); + jobject filter(env->NewLocalRef(mFilterObj)); + if (!env->IsSameObject(filter, nullptr)) { + env->CallVoidMethod( + filter, + gFields.onFilterStatusID, + (jint)status); + } else { + ALOGE("FilterClientCallbackImpl::onFilterStatus:" + "Filter object has been freed. Ignoring callback."); + } } void FilterClientCallbackImpl::setFilter(jweak filterObj, sp<FilterClient> filterClient) { @@ -841,6 +876,15 @@ void FilterClientCallbackImpl::setFilter(jweak filterObj, sp<FilterClient> filte mFilterClient = filterClient; } +FilterClientCallbackImpl::~FilterClientCallbackImpl() { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + if (mFilterObj != NULL) { + env->DeleteWeakGlobalRef(mFilterObj); + mFilterObj = NULL; + } + mFilterClient = NULL; +} + /////////////// FrontendClientCallbackImpl /////////////////////// FrontendClientCallbackImpl::FrontendClientCallbackImpl(jweak tunerObj) : mObject(tunerObj) {} @@ -848,10 +892,16 @@ FrontendClientCallbackImpl::FrontendClientCallbackImpl(jweak tunerObj) : mObject void FrontendClientCallbackImpl::onEvent(FrontendEventType frontendEventType) { ALOGD("FrontendClientCallbackImpl::onEvent, type=%d", frontendEventType); JNIEnv *env = AndroidRuntime::getJNIEnv(); - env->CallVoidMethod( - mObject, - gFields.onFrontendEventID, - (jint)frontendEventType); + jobject frontend(env->NewLocalRef(mObject)); + if (!env->IsSameObject(frontend, nullptr)) { + env->CallVoidMethod( + frontend, + gFields.onFrontendEventID, + (jint)frontendEventType); + } else { + ALOGE("FrontendClientCallbackImpl::onEvent:" + "Frontend object has been freed. Ignoring callback."); + } } void FrontendClientCallbackImpl::onScanMessage( @@ -859,11 +909,17 @@ void FrontendClientCallbackImpl::onScanMessage( ALOGD("FrontendClientCallbackImpl::onScanMessage, type=%d", type); JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass clazz = env->FindClass("android/media/tv/tuner/Tuner"); + jobject frontend(env->NewLocalRef(mObject)); + if (env->IsSameObject(frontend, nullptr)) { + ALOGE("FrontendClientCallbackImpl::onScanMessage:" + "Frontend object has been freed. Ignoring callback."); + return; + } switch(type) { case FrontendScanMessageType::LOCKED: { if (message.isLocked()) { env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onLocked", "()V")); } break; @@ -871,14 +927,14 @@ void FrontendClientCallbackImpl::onScanMessage( case FrontendScanMessageType::END: { if (message.isEnd()) { env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onScanStopped", "()V")); } break; } case FrontendScanMessageType::PROGRESS_PERCENT: { env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onProgress", "(I)V"), (jint) message.progressPercent()); break; @@ -889,7 +945,7 @@ void FrontendClientCallbackImpl::onScanMessage( env->SetIntArrayRegion(freqs, 0, v.size(), reinterpret_cast<jint*>(&v[0])); env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onFrequenciesReport", "([I)V"), freqs); break; @@ -900,21 +956,21 @@ void FrontendClientCallbackImpl::onScanMessage( env->SetIntArrayRegion(symbolRates, 0, v.size(), reinterpret_cast<jint*>(&v[0])); env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onSymbolRates", "([I)V"), symbolRates); break; } case FrontendScanMessageType::HIERARCHY: { env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onHierarchy", "(I)V"), (jint) message.hierarchy()); break; } case FrontendScanMessageType::ANALOG_TYPE: { env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onSignalType", "(I)V"), (jint) message.analogType()); break; @@ -926,7 +982,7 @@ void FrontendClientCallbackImpl::onScanMessage( env->SetIntArrayRegion(plpIds, 0, jintV.size(), &jintV[0]); env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onPlpIds", "([I)V"), plpIds); break; @@ -938,7 +994,7 @@ void FrontendClientCallbackImpl::onScanMessage( env->SetIntArrayRegion(groupIds, 0, jintV.size(), &jintV[0]); env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onGroupIds", "([I)V"), groupIds); break; @@ -950,7 +1006,7 @@ void FrontendClientCallbackImpl::onScanMessage( env->SetIntArrayRegion(streamIds, 0, jintV.size(), &jintV[0]); env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onInputStreamIds", "([I)V"), streamIds); break; @@ -961,21 +1017,21 @@ void FrontendClientCallbackImpl::onScanMessage( if (std.getDiscriminator() == FrontendScanMessage::Standard::hidl_discriminator::sStd) { standard = (jint) std.sStd(); env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onDvbsStandard", "(I)V"), standard); } else if (std.getDiscriminator() == FrontendScanMessage::Standard::hidl_discriminator::tStd) { standard = (jint) std.tStd(); env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onDvbtStandard", "(I)V"), standard); } else if (std.getDiscriminator() == FrontendScanMessage::Standard::hidl_discriminator::sifStd) { standard = (jint) std.sifStd(); env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onAnalogSifStandard", "(I)V"), standard); } @@ -996,7 +1052,7 @@ void FrontendClientCallbackImpl::onScanMessage( env->SetObjectArrayElement(array, i, obj); } env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onAtsc3PlpInfos", "([Landroid/media/tv/tuner/frontend/Atsc3PlpInfo;)V"), array); @@ -1010,6 +1066,12 @@ void FrontendClientCallbackImpl::onScanMessageExt1_1(FrontendScanMessageTypeExt1 ALOGD("FrontendClientCallbackImpl::onScanMessageExt1_1, type=%d", type); JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass clazz = env->FindClass("android/media/tv/tuner/Tuner"); + jobject frontend(env->NewLocalRef(mObject)); + if (env->IsSameObject(frontend, nullptr)) { + ALOGE("FrontendClientCallbackImpl::onScanMessageExt1_1:" + "Frontend object has been freed. Ignoring callback."); + return; + } switch(type) { case FrontendScanMessageTypeExt1_1::MODULATION: { jint modulation = -1; @@ -1056,7 +1118,7 @@ void FrontendClientCallbackImpl::onScanMessageExt1_1(FrontendScanMessageTypeExt1 } if (modulation > 0) { env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onModulationReported", "(I)V"), modulation); } @@ -1065,7 +1127,7 @@ void FrontendClientCallbackImpl::onScanMessageExt1_1(FrontendScanMessageTypeExt1 case FrontendScanMessageTypeExt1_1::HIGH_PRIORITY: { bool isHighPriority = message.isHighPriority(); env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onPriorityReported", "(B)V"), isHighPriority); break; @@ -1073,7 +1135,7 @@ void FrontendClientCallbackImpl::onScanMessageExt1_1(FrontendScanMessageTypeExt1 case FrontendScanMessageTypeExt1_1::DVBC_ANNEX: { jint dvbcAnnex = (jint) message.annex(); env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onDvbcAnnexReported", "(I)V"), dvbcAnnex); break; @@ -1083,6 +1145,14 @@ void FrontendClientCallbackImpl::onScanMessageExt1_1(FrontendScanMessageTypeExt1 } } +FrontendClientCallbackImpl::~FrontendClientCallbackImpl() { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + if (mObject != NULL) { + env->DeleteWeakGlobalRef(mObject); + mObject = NULL; + } +} + /////////////// Tuner /////////////////////// sp<TunerClient> JTuner::mTunerClient; @@ -1158,15 +1228,22 @@ jobject JTuner::openFrontendByHandle(int feHandle) { if (mDemuxClient != NULL) { mDemuxClient->setFrontendDataSource(mFeClient); } - sp<FrontendClientCallbackImpl> feClientCb = new FrontendClientCallbackImpl(mObject); - mFeClient->setCallback(feClientCb); JNIEnv *env = AndroidRuntime::getJNIEnv(); + jobject tuner(env->NewLocalRef(mObject)); + if (env->IsSameObject(tuner, nullptr)) { + ALOGE("openFrontendByHandle" + "Tuner object has been freed. Failed to open frontend."); + return NULL; + } + + sp<FrontendClientCallbackImpl> feClientCb = new FrontendClientCallbackImpl(mObject); + mFeClient->setCallback(feClientCb); // TODO: add more fields to frontend return env->NewObject( env->FindClass("android/media/tv/tuner/Tuner$Frontend"), gFields.frontendInitID, - mObject, + tuner, (jint) mFeId); } @@ -1714,16 +1791,14 @@ jobject JTuner::openDvr(DvrType type, jlong bufferSize) { dvrObj = env->NewObject( env->FindClass("android/media/tv/tuner/dvr/DvrRecorder"), - gFields.dvrRecorderInitID, - mObject); + gFields.dvrRecorderInitID); dvrClient->incStrong(dvrObj); env->SetLongField(dvrObj, gFields.dvrRecorderContext, (jlong)dvrClient.get()); } else { dvrObj = env->NewObject( env->FindClass("android/media/tv/tuner/dvr/DvrPlayback"), - gFields.dvrPlaybackInitID, - mObject); + gFields.dvrPlaybackInitID); dvrClient->incStrong(dvrObj); env->SetLongField(dvrObj, gFields.dvrPlaybackContext, (jlong)dvrClient.get()); } @@ -2126,6 +2201,10 @@ jobject JTuner::getFrontendStatus(jintArray types) { intBandwidth = static_cast<jint>(bandwidth.dvbt()); break; } + case FrontendBandwidth::hidl_discriminator::dvbc: { + intBandwidth = static_cast<jint>(bandwidth.dvbc()); + break; + } case FrontendBandwidth::hidl_discriminator::isdbt: { intBandwidth = static_cast<jint>(bandwidth.isdbt()); break; @@ -2646,12 +2725,10 @@ static FrontendSettings getDvbsFrontendSettings(JNIEnv *env, const jobject& sett FrontendDvbsVcmMode vcmMode = static_cast<FrontendDvbsVcmMode>( env->GetIntField(settings, env->GetFieldID(clazz, "mVcmMode", "I"))); - FrontendDvbsCodeRate coderate = getDvbsCodeRate(env, settings); FrontendDvbsSettings frontendDvbsSettings { .frequency = freq, .modulation = modulation, - .coderate = coderate, .symbolRate = symbolRate, .rolloff = rolloff, .pilot = pilot, @@ -2659,6 +2736,13 @@ static FrontendSettings getDvbsFrontendSettings(JNIEnv *env, const jobject& sett .standard = standard, .vcmMode = vcmMode, }; + + jobject jcodeRate = env->GetObjectField(settings, env->GetFieldID(clazz, "mCodeRate", + "Landroid/media/tv/tuner/frontend/DvbsCodeRate;")); + if (jcodeRate != NULL) { + frontendDvbsSettings.coderate = getDvbsCodeRate(env, settings); + } + frontendSettings.dvbs(frontendDvbsSettings); return frontendSettings; } @@ -2670,7 +2754,7 @@ static void getDvbsFrontendSettingsExt1_1(JNIEnv *env, const jobject& settings, static_cast<FrontendDvbsScanType>( env->GetIntField(settings, env->GetFieldID(clazz, "mScanType", "I"))); bool isDiseqcRxMessage = static_cast<bool>(env->GetBooleanField( - settings, env->GetFieldID(clazz, "mIsDiseqcRxMessage", "B"))); + settings, env->GetFieldID(clazz, "mIsDiseqcRxMessage", "Z"))); FrontendDvbsSettingsExt1_1 dvbsExt1_1 { .scanType = scanType, @@ -2910,6 +2994,10 @@ static void getDtmbFrontendSettings(JNIEnv *env, const jobject& settings, static FrontendSettings getFrontendSettings(JNIEnv *env, int type, jobject settings) { ALOGD("getFrontendSettings %d", type); + if (type == static_cast<int>(::android::hardware::tv::tuner::V1_1::FrontendType::DTMB)) { + return FrontendSettings(); + } + FrontendType feType = static_cast<FrontendType>(type); switch(feType) { case FrontendType::ANALOG: diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index 0e30b18eb2d4..fafef4221541 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -118,6 +118,7 @@ struct MediaEvent : public RefBase { }; struct FilterClientCallbackImpl : public FilterClientCallback { + ~FilterClientCallbackImpl(); virtual void onFilterEvent_1_1(const DemuxFilterEvent& filterEvent, const DemuxFilterEventExt& filterEventExt); virtual void onFilterEvent(const DemuxFilterEvent& filterEvent); @@ -155,7 +156,7 @@ private: struct FrontendClientCallbackImpl : public FrontendClientCallback { FrontendClientCallbackImpl(jweak tunerObj); - + ~FrontendClientCallbackImpl(); virtual void onEvent(FrontendEventType frontendEventType); virtual void onScanMessage( FrontendScanMessageType type, const FrontendScanMessage& message); diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp index 4efdcaccf7a1..ffed4747d3ea 100644 --- a/media/jni/android_mtp_MtpDatabase.cpp +++ b/media/jni/android_mtp_MtpDatabase.cpp @@ -32,6 +32,7 @@ #include <android_runtime/AndroidRuntime.h> #include <android_runtime/Log.h> +#include "core_jni_helpers.h" #include <jni.h> #include <media/stagefright/NuMediaExtractor.h> #include <nativehelper/JNIHelp.h> @@ -48,6 +49,7 @@ using namespace android; // ---------------------------------------------------------------------------- +// MtpDatabase methods static jmethodID method_beginSendObject; static jmethodID method_endSendObject; static jmethodID method_rescanFile; @@ -75,6 +77,7 @@ static jmethodID method_endCopyObject; static jmethodID method_getObjectReferences; static jmethodID method_setObjectReferences; +// MtpDatabase fields. static jfieldID field_context; // MtpPropertyList methods @@ -86,6 +89,59 @@ static jmethodID method_getDataTypes; static jmethodID method_getLongValues; static jmethodID method_getStringValues; +// Initializer for the jfieldIDs and jmethodIDs above. This method must be invoked +// before using these static fields and methods for JNI accesses. +static void initializeJavaIDs(JNIEnv* env) { + static std::once_flag sJniInitialized; + +#define GET_METHOD_ID(name, jclass, signature) \ + method_##name = GetMethodIDOrDie(env, jclass, #name, signature); + + std::call_once(sJniInitialized, [](JNIEnv* env) { + const jclass mdb_class = FindClassOrDie(env, "android/mtp/MtpDatabase"); + GET_METHOD_ID(beginSendObject, mdb_class, "(Ljava/lang/String;III)I"); + GET_METHOD_ID(endSendObject, mdb_class, "(IZ)V"); + GET_METHOD_ID(rescanFile, mdb_class, "(Ljava/lang/String;II)V"); + GET_METHOD_ID(getObjectList, mdb_class, "(III)[I"); + GET_METHOD_ID(getNumObjects, mdb_class, "(III)I"); + GET_METHOD_ID(getSupportedPlaybackFormats, mdb_class, "()[I"); + GET_METHOD_ID(getSupportedCaptureFormats, mdb_class, "()[I"); + GET_METHOD_ID(getSupportedObjectProperties, mdb_class, "(I)[I"); + GET_METHOD_ID(getSupportedDeviceProperties, mdb_class, "()[I"); + GET_METHOD_ID(setObjectProperty, mdb_class, "(IIJLjava/lang/String;)I"); + GET_METHOD_ID(getDeviceProperty, mdb_class, "(I[J[C)I"); + GET_METHOD_ID(setDeviceProperty, mdb_class, "(IJLjava/lang/String;)I"); + GET_METHOD_ID(getObjectPropertyList, mdb_class, "(IIIII)Landroid/mtp/MtpPropertyList;"); + GET_METHOD_ID(getObjectInfo, mdb_class, "(I[I[C[J)Z"); + GET_METHOD_ID(getObjectFilePath, mdb_class, "(I[C[J)I"); + GET_METHOD_ID(openFilePath, mdb_class, "(Ljava/lang/String;Z)I"); + GET_METHOD_ID(getThumbnailInfo, mdb_class, "(I[J)Z"); + GET_METHOD_ID(getThumbnailData, mdb_class, "(I)[B"); + GET_METHOD_ID(beginDeleteObject, mdb_class, "(I)I"); + GET_METHOD_ID(endDeleteObject, mdb_class, "(IZ)V"); + GET_METHOD_ID(beginMoveObject, mdb_class, "(III)I"); + GET_METHOD_ID(endMoveObject, mdb_class, "(IIIIIZ)V"); + GET_METHOD_ID(beginCopyObject, mdb_class, "(III)I"); + GET_METHOD_ID(endCopyObject, mdb_class, "(IZ)V"); + GET_METHOD_ID(getObjectReferences, mdb_class, "(I)[I"); + GET_METHOD_ID(setObjectReferences, mdb_class, "(I[I)I"); + field_context = GetFieldIDOrDie(env, mdb_class, "mNativeContext", "J"); + + const jclass mpl_class = FindClassOrDie(env, "android/mtp/MtpPropertyList"); + GET_METHOD_ID(getCode, mpl_class, "()I"); + GET_METHOD_ID(getCount, mpl_class, "()I"); + GET_METHOD_ID(getObjectHandles, mpl_class, "()[I"); + GET_METHOD_ID(getPropertyCodes, mpl_class, "()[I"); + GET_METHOD_ID(getDataTypes, mpl_class, "()[I"); + GET_METHOD_ID(getLongValues, mpl_class, "()[J"); + GET_METHOD_ID(getStringValues, mpl_class, "()[Ljava/lang/String;"); + + return 0; + }, env); + +#undef GET_METHOD_ID +} + IMtpDatabase* getMtpDatabase(JNIEnv *env, jobject database) { return (IMtpDatabase *)env->GetLongField(database, field_context); @@ -1280,6 +1336,7 @@ MtpProperty* MtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) { static void android_mtp_MtpDatabase_setup(JNIEnv *env, jobject thiz) { + initializeJavaIDs(env); MtpDatabase* database = new MtpDatabase(env, thiz); env->SetLongField(thiz, field_context, (jlong)database); checkAndClearExceptionFromCallback(env, __FUNCTION__); @@ -1314,69 +1371,9 @@ static const JNINativeMethod gMtpPropertyGroupMethods[] = { {"format_date_time", "(J)Ljava/lang/String;", (void *)android_mtp_MtpPropertyGroup_format_date_time}, }; - -#define GET_METHOD_ID(name, jclass, signature) \ - method_##name = env->GetMethodID(jclass, #name, signature); \ - if (method_##name == NULL) { \ - ALOGE("Can't find " #name); \ - return -1; \ - } \ - + \ int register_android_mtp_MtpDatabase(JNIEnv *env) { - jclass clazz; - - clazz = env->FindClass("android/mtp/MtpDatabase"); - if (clazz == NULL) { - ALOGE("Can't find android/mtp/MtpDatabase"); - return -1; - } - GET_METHOD_ID(beginSendObject, clazz, "(Ljava/lang/String;III)I"); - GET_METHOD_ID(endSendObject, clazz, "(IZ)V"); - GET_METHOD_ID(rescanFile, clazz, "(Ljava/lang/String;II)V"); - GET_METHOD_ID(getObjectList, clazz, "(III)[I"); - GET_METHOD_ID(getNumObjects, clazz, "(III)I"); - GET_METHOD_ID(getSupportedPlaybackFormats, clazz, "()[I"); - GET_METHOD_ID(getSupportedCaptureFormats, clazz, "()[I"); - GET_METHOD_ID(getSupportedObjectProperties, clazz, "(I)[I"); - GET_METHOD_ID(getSupportedDeviceProperties, clazz, "()[I"); - GET_METHOD_ID(setObjectProperty, clazz, "(IIJLjava/lang/String;)I"); - GET_METHOD_ID(getDeviceProperty, clazz, "(I[J[C)I"); - GET_METHOD_ID(setDeviceProperty, clazz, "(IJLjava/lang/String;)I"); - GET_METHOD_ID(getObjectPropertyList, clazz, "(IIIII)Landroid/mtp/MtpPropertyList;"); - GET_METHOD_ID(getObjectInfo, clazz, "(I[I[C[J)Z"); - GET_METHOD_ID(getObjectFilePath, clazz, "(I[C[J)I"); - GET_METHOD_ID(openFilePath, clazz, "(Ljava/lang/String;Z)I"); - GET_METHOD_ID(getThumbnailInfo, clazz, "(I[J)Z"); - GET_METHOD_ID(getThumbnailData, clazz, "(I)[B"); - GET_METHOD_ID(beginDeleteObject, clazz, "(I)I"); - GET_METHOD_ID(endDeleteObject, clazz, "(IZ)V"); - GET_METHOD_ID(beginMoveObject, clazz, "(III)I"); - GET_METHOD_ID(endMoveObject, clazz, "(IIIIIZ)V"); - GET_METHOD_ID(beginCopyObject, clazz, "(III)I"); - GET_METHOD_ID(endCopyObject, clazz, "(IZ)V"); - GET_METHOD_ID(getObjectReferences, clazz, "(I)[I"); - GET_METHOD_ID(setObjectReferences, clazz, "(I[I)I"); - - field_context = env->GetFieldID(clazz, "mNativeContext", "J"); - if (field_context == NULL) { - ALOGE("Can't find MtpDatabase.mNativeContext"); - return -1; - } - - clazz = env->FindClass("android/mtp/MtpPropertyList"); - if (clazz == NULL) { - ALOGE("Can't find android/mtp/MtpPropertyList"); - return -1; - } - GET_METHOD_ID(getCode, clazz, "()I"); - GET_METHOD_ID(getCount, clazz, "()I"); - GET_METHOD_ID(getObjectHandles, clazz, "()[I"); - GET_METHOD_ID(getPropertyCodes, clazz, "()[I"); - GET_METHOD_ID(getDataTypes, clazz, "()[I"); - GET_METHOD_ID(getLongValues, clazz, "()[J"); - GET_METHOD_ID(getStringValues, clazz, "()[Ljava/lang/String;"); - if (AndroidRuntime::registerNativeMethods(env, "android/mtp/MtpDatabase", gMtpDatabaseMethods, NELEM(gMtpDatabaseMethods))) return -1; diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp index 060eaf9ccad4..3d2b00fec26c 100644 --- a/media/jni/android_mtp_MtpDevice.cpp +++ b/media/jni/android_mtp_MtpDevice.cpp @@ -34,6 +34,7 @@ #include "android_runtime/AndroidRuntime.h" #include "android_runtime/Log.h" +#include "core_jni_helpers.h" #include "nativehelper/ScopedLocalRef.h" #include "private/android_filesystem_config.h" @@ -107,6 +108,95 @@ static jfieldID field_event_parameter1; static jfieldID field_event_parameter2; static jfieldID field_event_parameter3; +// Initializer for the jclasses, jfieldIDs and jmethodIDs declared above. This method must be +// invoked before using these static fields for JNI accesses. +static void initializeJavaIDs(JNIEnv* env) { + static std::once_flag sJniInitialized; + + std::call_once(sJniInitialized, [](JNIEnv* env) { + clazz_deviceInfo = + (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpDeviceInfo")); + constructor_deviceInfo = GetMethodIDOrDie(env, clazz_deviceInfo, "<init>", "()V"); + field_deviceInfo_manufacturer = + GetFieldIDOrDie(env, clazz_deviceInfo, "mManufacturer", "Ljava/lang/String;"); + field_deviceInfo_model = + GetFieldIDOrDie(env, clazz_deviceInfo, "mModel", "Ljava/lang/String;"); + field_deviceInfo_version = + GetFieldIDOrDie(env, clazz_deviceInfo, "mVersion", "Ljava/lang/String;"); + field_deviceInfo_serialNumber = + GetFieldIDOrDie(env, clazz_deviceInfo, "mSerialNumber", "Ljava/lang/String;"); + field_deviceInfo_operationsSupported = + GetFieldIDOrDie(env, clazz_deviceInfo, "mOperationsSupported", "[I"); + field_deviceInfo_eventsSupported = + GetFieldIDOrDie(env, clazz_deviceInfo, "mEventsSupported", "[I"); + + clazz_storageInfo = + (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpStorageInfo")); + constructor_storageInfo = GetMethodIDOrDie(env, clazz_storageInfo, "<init>", "()V"); + field_storageInfo_storageId = GetFieldIDOrDie(env, clazz_storageInfo, "mStorageId", "I"); + field_storageInfo_maxCapacity = + GetFieldIDOrDie(env, clazz_storageInfo, "mMaxCapacity", "J"); + field_storageInfo_freeSpace = + GetFieldIDOrDie(env, clazz_storageInfo, "mFreeSpace", "J"); + field_storageInfo_description = + GetFieldIDOrDie(env, clazz_storageInfo, "mDescription", "Ljava/lang/String;"); + field_storageInfo_volumeIdentifier = + GetFieldIDOrDie(env, clazz_storageInfo, "mVolumeIdentifier", "Ljava/lang/String;"); + + clazz_objectInfo = + (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpObjectInfo")); + constructor_objectInfo = GetMethodIDOrDie(env, clazz_objectInfo, "<init>", "()V"); + field_objectInfo_handle = GetFieldIDOrDie(env, clazz_objectInfo, "mHandle", "I"); + field_objectInfo_storageId = GetFieldIDOrDie(env, clazz_objectInfo, "mStorageId", "I"); + field_objectInfo_format = GetFieldIDOrDie(env, clazz_objectInfo, "mFormat", "I"); + field_objectInfo_protectionStatus = + GetFieldIDOrDie(env, clazz_objectInfo, "mProtectionStatus", "I"); + field_objectInfo_compressedSize = + GetFieldIDOrDie(env, clazz_objectInfo, "mCompressedSize", "I"); + field_objectInfo_thumbFormat = GetFieldIDOrDie(env, clazz_objectInfo, "mThumbFormat", "I"); + field_objectInfo_thumbCompressedSize = + GetFieldIDOrDie(env, clazz_objectInfo, "mThumbCompressedSize", "I"); + field_objectInfo_thumbPixWidth = + GetFieldIDOrDie(env, clazz_objectInfo, "mThumbPixWidth", "I"); + field_objectInfo_thumbPixHeight = + GetFieldIDOrDie(env, clazz_objectInfo, "mThumbPixHeight", "I"); + field_objectInfo_imagePixWidth = + GetFieldIDOrDie(env, clazz_objectInfo, "mImagePixWidth", "I"); + field_objectInfo_imagePixHeight = + GetFieldIDOrDie(env, clazz_objectInfo, "mImagePixHeight", "I"); + field_objectInfo_imagePixDepth = + GetFieldIDOrDie(env, clazz_objectInfo, "mImagePixDepth", "I"); + field_objectInfo_parent = GetFieldIDOrDie(env, clazz_objectInfo, "mParent", "I"); + field_objectInfo_associationType = + GetFieldIDOrDie(env, clazz_objectInfo, "mAssociationType", "I"); + field_objectInfo_associationDesc = + GetFieldIDOrDie(env, clazz_objectInfo, "mAssociationDesc", "I"); + field_objectInfo_sequenceNumber = + GetFieldIDOrDie(env, clazz_objectInfo, "mSequenceNumber", "I"); + field_objectInfo_name = + GetFieldIDOrDie(env, clazz_objectInfo, "mName", "Ljava/lang/String;"); + field_objectInfo_dateCreated = GetFieldIDOrDie(env, clazz_objectInfo, "mDateCreated", "J"); + field_objectInfo_dateModified = + GetFieldIDOrDie(env, clazz_objectInfo, "mDateModified", "J"); + field_objectInfo_keywords = + GetFieldIDOrDie(env, clazz_objectInfo, "mKeywords", "Ljava/lang/String;"); + + clazz_event = (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpEvent")); + constructor_event = GetMethodIDOrDie(env, clazz_event, "<init>", "()V"); + field_event_eventCode = GetFieldIDOrDie(env, clazz_event, "mEventCode", "I"); + field_event_parameter1 = GetFieldIDOrDie(env, clazz_event, "mParameter1", "I"); + field_event_parameter2 = GetFieldIDOrDie(env, clazz_event, "mParameter2", "I"); + field_event_parameter3 = GetFieldIDOrDie(env, clazz_event, "mParameter3", "I"); + + const jclass clazz = FindClassOrDie(env, "android/mtp/MtpDevice"); + field_context = GetFieldIDOrDie(env, clazz, "mNativeContext", "J"); + + clazz_io_exception = (jclass)env->NewGlobalRef(FindClassOrDie(env, "java/io/IOException")); + clazz_operation_canceled_exception = + (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/os/OperationCanceledException")); + }, env); +} + class JavaArrayWriter { public: JavaArrayWriter(JNIEnv* env, jbyteArray array) : @@ -132,6 +222,11 @@ private: MtpDevice* get_device_from_object(JNIEnv* env, jobject javaDevice) { + // get_device_from_object() is called by the majority of JNI methods in this file. Call + // `initializeJavaIDs()` here to ensure JNI methodID's, fieldIDs and classes are initialized + // before use. + initializeJavaIDs(env); + return (MtpDevice*)env->GetLongField(javaDevice, field_context); } @@ -200,6 +295,8 @@ android_mtp_MtpDevice_open(JNIEnv *env, jobject thiz, jstring deviceName, jint f MtpDevice* device = MtpDevice::open(deviceNameStr, fd); env->ReleaseStringUTFChars(deviceName, deviceNameStr); + // The jfieldID `field_context` needs to be initialized before use below. + initializeJavaIDs(env); if (device) env->SetLongField(thiz, field_context, (jlong)device); return (jboolean)(device != NULL); @@ -781,256 +878,7 @@ static const JNINativeMethod gMethods[] = { int register_android_mtp_MtpDevice(JNIEnv *env) { - jclass clazz; - ALOGD("register_android_mtp_MtpDevice\n"); - - clazz = env->FindClass("android/mtp/MtpDeviceInfo"); - if (clazz == NULL) { - ALOGE("Can't find android/mtp/MtpDeviceInfo"); - return -1; - } - constructor_deviceInfo = env->GetMethodID(clazz, "<init>", "()V"); - if (constructor_deviceInfo == NULL) { - ALOGE("Can't find android/mtp/MtpDeviceInfo constructor"); - return -1; - } - field_deviceInfo_manufacturer = env->GetFieldID(clazz, "mManufacturer", "Ljava/lang/String;"); - if (field_deviceInfo_manufacturer == NULL) { - ALOGE("Can't find MtpDeviceInfo.mManufacturer"); - return -1; - } - field_deviceInfo_model = env->GetFieldID(clazz, "mModel", "Ljava/lang/String;"); - if (field_deviceInfo_model == NULL) { - ALOGE("Can't find MtpDeviceInfo.mModel"); - return -1; - } - field_deviceInfo_version = env->GetFieldID(clazz, "mVersion", "Ljava/lang/String;"); - if (field_deviceInfo_version == NULL) { - ALOGE("Can't find MtpDeviceInfo.mVersion"); - return -1; - } - field_deviceInfo_serialNumber = env->GetFieldID(clazz, "mSerialNumber", "Ljava/lang/String;"); - if (field_deviceInfo_serialNumber == NULL) { - ALOGE("Can't find MtpDeviceInfo.mSerialNumber"); - return -1; - } - field_deviceInfo_operationsSupported = env->GetFieldID(clazz, "mOperationsSupported", "[I"); - if (field_deviceInfo_operationsSupported == NULL) { - ALOGE("Can't find MtpDeviceInfo.mOperationsSupported"); - return -1; - } - field_deviceInfo_eventsSupported = env->GetFieldID(clazz, "mEventsSupported", "[I"); - if (field_deviceInfo_eventsSupported == NULL) { - ALOGE("Can't find MtpDeviceInfo.mEventsSupported"); - return -1; - } - clazz_deviceInfo = (jclass)env->NewGlobalRef(clazz); - - clazz = env->FindClass("android/mtp/MtpStorageInfo"); - if (clazz == NULL) { - ALOGE("Can't find android/mtp/MtpStorageInfo"); - return -1; - } - constructor_storageInfo = env->GetMethodID(clazz, "<init>", "()V"); - if (constructor_storageInfo == NULL) { - ALOGE("Can't find android/mtp/MtpStorageInfo constructor"); - return -1; - } - field_storageInfo_storageId = env->GetFieldID(clazz, "mStorageId", "I"); - if (field_storageInfo_storageId == NULL) { - ALOGE("Can't find MtpStorageInfo.mStorageId"); - return -1; - } - field_storageInfo_maxCapacity = env->GetFieldID(clazz, "mMaxCapacity", "J"); - if (field_storageInfo_maxCapacity == NULL) { - ALOGE("Can't find MtpStorageInfo.mMaxCapacity"); - return -1; - } - field_storageInfo_freeSpace = env->GetFieldID(clazz, "mFreeSpace", "J"); - if (field_storageInfo_freeSpace == NULL) { - ALOGE("Can't find MtpStorageInfo.mFreeSpace"); - return -1; - } - field_storageInfo_description = env->GetFieldID(clazz, "mDescription", "Ljava/lang/String;"); - if (field_storageInfo_description == NULL) { - ALOGE("Can't find MtpStorageInfo.mDescription"); - return -1; - } - field_storageInfo_volumeIdentifier = env->GetFieldID(clazz, "mVolumeIdentifier", "Ljava/lang/String;"); - if (field_storageInfo_volumeIdentifier == NULL) { - ALOGE("Can't find MtpStorageInfo.mVolumeIdentifier"); - return -1; - } - clazz_storageInfo = (jclass)env->NewGlobalRef(clazz); - - clazz = env->FindClass("android/mtp/MtpObjectInfo"); - if (clazz == NULL) { - ALOGE("Can't find android/mtp/MtpObjectInfo"); - return -1; - } - constructor_objectInfo = env->GetMethodID(clazz, "<init>", "()V"); - if (constructor_objectInfo == NULL) { - ALOGE("Can't find android/mtp/MtpObjectInfo constructor"); - return -1; - } - field_objectInfo_handle = env->GetFieldID(clazz, "mHandle", "I"); - if (field_objectInfo_handle == NULL) { - ALOGE("Can't find MtpObjectInfo.mHandle"); - return -1; - } - field_objectInfo_storageId = env->GetFieldID(clazz, "mStorageId", "I"); - if (field_objectInfo_storageId == NULL) { - ALOGE("Can't find MtpObjectInfo.mStorageId"); - return -1; - } - field_objectInfo_format = env->GetFieldID(clazz, "mFormat", "I"); - if (field_objectInfo_format == NULL) { - ALOGE("Can't find MtpObjectInfo.mFormat"); - return -1; - } - field_objectInfo_protectionStatus = env->GetFieldID(clazz, "mProtectionStatus", "I"); - if (field_objectInfo_protectionStatus == NULL) { - ALOGE("Can't find MtpObjectInfo.mProtectionStatus"); - return -1; - } - field_objectInfo_compressedSize = env->GetFieldID(clazz, "mCompressedSize", "I"); - if (field_objectInfo_compressedSize == NULL) { - ALOGE("Can't find MtpObjectInfo.mCompressedSize"); - return -1; - } - field_objectInfo_thumbFormat = env->GetFieldID(clazz, "mThumbFormat", "I"); - if (field_objectInfo_thumbFormat == NULL) { - ALOGE("Can't find MtpObjectInfo.mThumbFormat"); - return -1; - } - field_objectInfo_thumbCompressedSize = env->GetFieldID(clazz, "mThumbCompressedSize", "I"); - if (field_objectInfo_thumbCompressedSize == NULL) { - ALOGE("Can't find MtpObjectInfo.mThumbCompressedSize"); - return -1; - } - field_objectInfo_thumbPixWidth = env->GetFieldID(clazz, "mThumbPixWidth", "I"); - if (field_objectInfo_thumbPixWidth == NULL) { - ALOGE("Can't find MtpObjectInfo.mThumbPixWidth"); - return -1; - } - field_objectInfo_thumbPixHeight = env->GetFieldID(clazz, "mThumbPixHeight", "I"); - if (field_objectInfo_thumbPixHeight == NULL) { - ALOGE("Can't find MtpObjectInfo.mThumbPixHeight"); - return -1; - } - field_objectInfo_imagePixWidth = env->GetFieldID(clazz, "mImagePixWidth", "I"); - if (field_objectInfo_imagePixWidth == NULL) { - ALOGE("Can't find MtpObjectInfo.mImagePixWidth"); - return -1; - } - field_objectInfo_imagePixHeight = env->GetFieldID(clazz, "mImagePixHeight", "I"); - if (field_objectInfo_imagePixHeight == NULL) { - ALOGE("Can't find MtpObjectInfo.mImagePixHeight"); - return -1; - } - field_objectInfo_imagePixDepth = env->GetFieldID(clazz, "mImagePixDepth", "I"); - if (field_objectInfo_imagePixDepth == NULL) { - ALOGE("Can't find MtpObjectInfo.mImagePixDepth"); - return -1; - } - field_objectInfo_parent = env->GetFieldID(clazz, "mParent", "I"); - if (field_objectInfo_parent == NULL) { - ALOGE("Can't find MtpObjectInfo.mParent"); - return -1; - } - field_objectInfo_associationType = env->GetFieldID(clazz, "mAssociationType", "I"); - if (field_objectInfo_associationType == NULL) { - ALOGE("Can't find MtpObjectInfo.mAssociationType"); - return -1; - } - field_objectInfo_associationDesc = env->GetFieldID(clazz, "mAssociationDesc", "I"); - if (field_objectInfo_associationDesc == NULL) { - ALOGE("Can't find MtpObjectInfo.mAssociationDesc"); - return -1; - } - field_objectInfo_sequenceNumber = env->GetFieldID(clazz, "mSequenceNumber", "I"); - if (field_objectInfo_sequenceNumber == NULL) { - ALOGE("Can't find MtpObjectInfo.mSequenceNumber"); - return -1; - } - field_objectInfo_name = env->GetFieldID(clazz, "mName", "Ljava/lang/String;"); - if (field_objectInfo_name == NULL) { - ALOGE("Can't find MtpObjectInfo.mName"); - return -1; - } - field_objectInfo_dateCreated = env->GetFieldID(clazz, "mDateCreated", "J"); - if (field_objectInfo_dateCreated == NULL) { - ALOGE("Can't find MtpObjectInfo.mDateCreated"); - return -1; - } - field_objectInfo_dateModified = env->GetFieldID(clazz, "mDateModified", "J"); - if (field_objectInfo_dateModified == NULL) { - ALOGE("Can't find MtpObjectInfo.mDateModified"); - return -1; - } - field_objectInfo_keywords = env->GetFieldID(clazz, "mKeywords", "Ljava/lang/String;"); - if (field_objectInfo_keywords == NULL) { - ALOGE("Can't find MtpObjectInfo.mKeywords"); - return -1; - } - clazz_objectInfo = (jclass)env->NewGlobalRef(clazz); - - clazz = env->FindClass("android/mtp/MtpEvent"); - if (clazz == NULL) { - ALOGE("Can't find android/mtp/MtpEvent"); - return -1; - } - constructor_event = env->GetMethodID(clazz, "<init>", "()V"); - if (constructor_event == NULL) { - ALOGE("Can't find android/mtp/MtpEvent constructor"); - return -1; - } - field_event_eventCode = env->GetFieldID(clazz, "mEventCode", "I"); - if (field_event_eventCode == NULL) { - ALOGE("Can't find MtpObjectInfo.mEventCode"); - return -1; - } - field_event_parameter1 = env->GetFieldID(clazz, "mParameter1", "I"); - if (field_event_parameter1 == NULL) { - ALOGE("Can't find MtpObjectInfo.mParameter1"); - return -1; - } - field_event_parameter2 = env->GetFieldID(clazz, "mParameter2", "I"); - if (field_event_parameter2 == NULL) { - ALOGE("Can't find MtpObjectInfo.mParameter2"); - return -1; - } - field_event_parameter3 = env->GetFieldID(clazz, "mParameter3", "I"); - if (field_event_parameter3 == NULL) { - ALOGE("Can't find MtpObjectInfo.mParameter3"); - return -1; - } - clazz_event = (jclass)env->NewGlobalRef(clazz); - - clazz = env->FindClass("android/mtp/MtpDevice"); - if (clazz == NULL) { - ALOGE("Can't find android/mtp/MtpDevice"); - return -1; - } - field_context = env->GetFieldID(clazz, "mNativeContext", "J"); - if (field_context == NULL) { - ALOGE("Can't find MtpDevice.mNativeContext"); - return -1; - } - clazz = env->FindClass("java/io/IOException"); - if (clazz == NULL) { - ALOGE("Can't find java.io.IOException"); - return -1; - } - clazz_io_exception = (jclass)env->NewGlobalRef(clazz); - clazz = env->FindClass("android/os/OperationCanceledException"); - if (clazz == NULL) { - ALOGE("Can't find android.os.OperationCanceledException"); - return -1; - } - clazz_operation_canceled_exception = (jclass)env->NewGlobalRef(clazz); - return AndroidRuntime::registerNativeMethods(env, "android/mtp/MtpDevice", gMethods, NELEM(gMethods)); } diff --git a/media/jni/android_mtp_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp index 8a1ae9252ca3..b4844f79b540 100644 --- a/media/jni/android_mtp_MtpServer.cpp +++ b/media/jni/android_mtp_MtpServer.cpp @@ -24,6 +24,7 @@ #include <fcntl.h> #include <utils/threads.h> +#include "core_jni_helpers.h" #include "jni.h" #include <nativehelper/JNIPlatformHelp.h> #include "android_runtime/AndroidRuntime.h" @@ -34,6 +35,8 @@ using namespace android; +static Mutex sMutex; + // MtpServer fields static jfieldID field_MtpServer_nativeContext; @@ -44,7 +47,25 @@ static jfieldID field_MtpStorage_description; static jfieldID field_MtpStorage_removable; static jfieldID field_MtpStorage_maxFileSize; -static Mutex sMutex; +// Initializer for the jfieldIDs above. This method must be invoked before accessing MtpServer and +// MtpStorage fields. +static void initializeJavaIDs(JNIEnv* env) { + static std::once_flag sJniInitialized; + + std::call_once(sJniInitialized, [](JNIEnv *env) { + const jclass storage_clazz = FindClassOrDie(env, "android/mtp/MtpStorage"); + field_MtpStorage_storageId = GetFieldIDOrDie(env, storage_clazz, "mStorageId", "I"); + field_MtpStorage_path = + GetFieldIDOrDie(env, storage_clazz, "mPath", "Ljava/lang/String;"); + field_MtpStorage_description = + GetFieldIDOrDie(env, storage_clazz, "mDescription", "Ljava/lang/String;"); + field_MtpStorage_removable = GetFieldIDOrDie(env, storage_clazz, "mRemovable", "Z"); + field_MtpStorage_maxFileSize = GetFieldIDOrDie(env, storage_clazz, "mMaxFileSize", "J"); + + const jclass server_clazz = FindClassOrDie(env, "android/mtp/MtpServer"); + field_MtpServer_nativeContext = GetFieldIDOrDie(env, server_clazz, "mNativeContext", "J"); + }, env); +} // ---------------------------------------------------------------------------- @@ -52,6 +73,7 @@ static Mutex sMutex; extern IMtpDatabase* getMtpDatabase(JNIEnv *env, jobject database); static inline MtpServer* getMtpServer(JNIEnv *env, jobject thiz) { + initializeJavaIDs(env); return (MtpServer*)env->GetLongField(thiz, field_MtpServer_nativeContext); } @@ -60,6 +82,8 @@ android_mtp_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase, job jboolean usePtp, jstring deviceInfoManufacturer, jstring deviceInfoModel, jstring deviceInfoDeviceVersion, jstring deviceInfoSerialNumber) { + initializeJavaIDs(env); + const char *deviceInfoManufacturerStr = env->GetStringUTFChars(deviceInfoManufacturer, NULL); const char *deviceInfoModelStr = env->GetStringUTFChars(deviceInfoModel, NULL); const char *deviceInfoDeviceVersionStr = env->GetStringUTFChars(deviceInfoDeviceVersion, NULL); @@ -224,50 +248,6 @@ static const JNINativeMethod gMethods[] = { int register_android_mtp_MtpServer(JNIEnv *env) { - jclass clazz; - - clazz = env->FindClass("android/mtp/MtpStorage"); - if (clazz == NULL) { - ALOGE("Can't find android/mtp/MtpStorage"); - return -1; - } - field_MtpStorage_storageId = env->GetFieldID(clazz, "mStorageId", "I"); - if (field_MtpStorage_storageId == NULL) { - ALOGE("Can't find MtpStorage.mStorageId"); - return -1; - } - field_MtpStorage_path = env->GetFieldID(clazz, "mPath", "Ljava/lang/String;"); - if (field_MtpStorage_path == NULL) { - ALOGE("Can't find MtpStorage.mPath"); - return -1; - } - field_MtpStorage_description = env->GetFieldID(clazz, "mDescription", "Ljava/lang/String;"); - if (field_MtpStorage_description == NULL) { - ALOGE("Can't find MtpStorage.mDescription"); - return -1; - } - field_MtpStorage_removable = env->GetFieldID(clazz, "mRemovable", "Z"); - if (field_MtpStorage_removable == NULL) { - ALOGE("Can't find MtpStorage.mRemovable"); - return -1; - } - field_MtpStorage_maxFileSize = env->GetFieldID(clazz, "mMaxFileSize", "J"); - if (field_MtpStorage_maxFileSize == NULL) { - ALOGE("Can't find MtpStorage.mMaxFileSize"); - return -1; - } - - clazz = env->FindClass("android/mtp/MtpServer"); - if (clazz == NULL) { - ALOGE("Can't find android/mtp/MtpServer"); - return -1; - } - field_MtpServer_nativeContext = env->GetFieldID(clazz, "mNativeContext", "J"); - if (field_MtpServer_nativeContext == NULL) { - ALOGE("Can't find MtpServer.mNativeContext"); - return -1; - } - return AndroidRuntime::registerNativeMethods(env, "android/mtp/MtpServer", gMethods, NELEM(gMethods)); } diff --git a/media/jni/soundpool/Android.bp b/media/jni/soundpool/Android.bp index 7b498e027d1c..b3406cd89046 100644 --- a/media/jni/soundpool/Android.bp +++ b/media/jni/soundpool/Android.bp @@ -45,26 +45,30 @@ tidy_errors = [ "modernize-return-braced-init-list", "modernize-shrink-to-fit", "modernize-unary-static-assert", - "modernize-use-auto", // debatable - auto can obscure type + // "modernize-use-auto", // found in StreamManager.h, debatable - auto can obscure type "modernize-use-bool-literals", "modernize-use-default-member-init", "modernize-use-emplace", "modernize-use-equals-default", "modernize-use-equals-delete", - "modernize-use-nodiscard", + // "modernize-use-nodiscard", // found in SteamManager.h "modernize-use-noexcept", "modernize-use-nullptr", "modernize-use-override", //"modernize-use-trailing-return-type", // not necessarily more readable "modernize-use-transparent-functors", "modernize-use-uncaught-exceptions", - "modernize-use-using", + //"modernize-use-using", // found in SoundManager.h "performance-*", // Remove some pedantic stylistic requirements. "-google-readability-casting", // C++ casts not always necessary and may be verbose "-google-readability-todo", // do not require TODO(info) "-google-build-using-namespace", // Reenable and fix later. + + "-google-explicit-constructor", // found in StreamManager.h + "-misc-non-private-member-variables-in-classes", // found in SoundManager.h + "-performance-unnecessary-value-param", // found in StreamManager.h ] cc_defaults { @@ -96,8 +100,7 @@ cc_defaults { tidy_checks: tidy_errors, tidy_checks_as_errors: tidy_errors, tidy_flags: [ - "-format-style='file'", - "--header-filter='frameworks/base/media/jni/soundpool'", + "-format-style=file", ], } diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp index f400a7157051..f54e2663843c 100644 --- a/media/jni/tuner/FrontendClient.cpp +++ b/media/jni/tuner/FrontendClient.cpp @@ -588,14 +588,15 @@ vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt( break; } case TunerFrontendStatus::codeRates: { - int size = s.get<TunerFrontendStatus::codeRates>().size(); - status.codeRates().resize(size); - for (int i = 0; i < size; i++) { - auto aidlCodeRate = s.get<TunerFrontendStatus::codeRates>()[i]; - status.codeRates()[i] = - static_cast<hardware::tv::tuner::V1_1::FrontendInnerFec>(aidlCodeRate); + vector<hardware::tv::tuner::V1_1::FrontendInnerFec> codeRates; + for (auto aidlCodeRate : s.get<TunerFrontendStatus::codeRates>()) { + codeRates.push_back( + static_cast<hardware::tv::tuner::V1_1::FrontendInnerFec>(aidlCodeRate)); + } + if (codeRates.size() > 0) { + status.codeRates(codeRates); + hidlStatus.push_back(status); } - hidlStatus.push_back(status); break; } case TunerFrontendStatus::bandwidth: { diff --git a/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp b/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp index 1a51218616d2..fdca3fad2383 100644 --- a/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp +++ b/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test_helper_app { name: "UidImportanceHelperApp", manifest: "HelperAppManifest.xml", @@ -8,4 +17,3 @@ android_test_helper_app { "general-tests", ], } - diff --git a/native/android/tests/activitymanager/nativeTests/Android.bp b/native/android/tests/activitymanager/nativeTests/Android.bp index d4b5015ad8f3..528ac12c16b7 100644 --- a/native/android/tests/activitymanager/nativeTests/Android.bp +++ b/native/android/tests/activitymanager/nativeTests/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + cc_test { name: "ActivityManagerNativeTestCases", diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml index 91bad7b4aa91..f9795c601431 100644 --- a/packages/CompanionDeviceManager/AndroidManifest.xml +++ b/packages/CompanionDeviceManager/AndroidManifest.xml @@ -41,13 +41,13 @@ android:supportsRtl="true"> <service - android:name=".DeviceDiscoveryService" + android:name=".CompanionDeviceDiscoveryService" android:permission="android.permission.BIND_COMPANION_DEVICE_MANAGER_SERVICE" android:exported="true"> </service> <activity - android:name=".DeviceChooserActivity" + android:name=".CompanionDeviceActivity" android:theme="@style/ChooserActivity" android:permission="android.permission.BIND_COMPANION_DEVICE_MANAGER_SERVICE" android:exported="true"> diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java index c30d4bf322cf..28072ca4f113 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java @@ -24,6 +24,7 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTE import static java.util.Objects.requireNonNull; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; import android.companion.AssociationRequest; @@ -31,41 +32,52 @@ import android.companion.CompanionDeviceManager; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Resources; +import android.content.res.TypedArray; import android.database.DataSetObserver; +import android.graphics.Color; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.text.Html; import android.util.Log; +import android.util.SparseArray; +import android.util.TypedValue; import android.view.Gravity; import android.view.View; -import android.widget.AdapterView; +import android.view.ViewGroup; +import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; -import com.android.companiondevicemanager.DeviceDiscoveryService.DeviceFilterPair; +import com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DeviceFilterPair; import com.android.internal.util.Preconditions; -public class DeviceChooserActivity extends Activity { +public class CompanionDeviceActivity extends Activity { private static final boolean DEBUG = false; - private static final String LOG_TAG = "DeviceChooserActivity"; + private static final String LOG_TAG = CompanionDeviceActivity.class.getSimpleName(); + + static CompanionDeviceActivity sInstance; View mLoadingIndicator = null; ListView mDeviceListView; private View mPairButton; private View mCancelButton; + DevicesAdapter mDevicesAdapter; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (DEBUG) Log.i(LOG_TAG, "Started with intent " + getIntent()); + Log.i(LOG_TAG, "Starting UI for " + getService().mRequest); if (getService().mDevicesFound.isEmpty()) { Log.e(LOG_TAG, "About to show UI, but no devices to show"); } getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); + sInstance = this; String deviceProfile = getRequest().getDeviceProfile(); String profilePrivacyDisclaimer = emptyIfNull(getRequest() @@ -96,17 +108,14 @@ public class DeviceChooserActivity extends Activity { profileName, getCallingAppName()), 0)); mDeviceListView = findViewById(R.id.device_list); - final DeviceDiscoveryService.DevicesAdapter adapter = getService().mDevicesAdapter; - mDeviceListView.setAdapter(adapter); - mDeviceListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView<?> adapterView, View view, int pos, long l) { - getService().mSelectedDevice = - (DeviceFilterPair) adapterView.getItemAtPosition(pos); - adapter.notifyDataSetChanged(); - } + mDevicesAdapter = new DevicesAdapter(); + mDeviceListView.setAdapter(mDevicesAdapter); + mDeviceListView.setOnItemClickListener((adapterView, view, pos, l) -> { + getService().mSelectedDevice = + (DeviceFilterPair) adapterView.getItemAtPosition(pos); + mDevicesAdapter.notifyDataSetChanged(); }); - adapter.registerDataSetObserver(new DataSetObserver() { + mDevicesAdapter.registerDataSetObserver(new DataSetObserver() { @Override public void onChanged() { onSelectionUpdate(); @@ -133,6 +142,12 @@ public class DeviceChooserActivity extends Activity { mCancelButton.setOnClickListener(v -> cancel()); } + static void notifyDevicesChanged() { + if (sInstance != null && !sInstance.isFinishing()) { + sInstance.mDevicesAdapter.notifyDataSetChanged(); + } + } + private AssociationRequest getRequest() { return getService().mRequest; } @@ -156,6 +171,7 @@ public class DeviceChooserActivity extends Activity { } private void cancel() { + Log.i(LOG_TAG, "cancel()"); getService().onCancel(); setResult(RESULT_CANCELED); finish(); @@ -170,6 +186,14 @@ public class DeviceChooserActivity extends Activity { } } + @Override + protected void onDestroy() { + super.onDestroy(); + if (sInstance == this) { + sInstance = null; + } + } + private CharSequence getCallingAppName() { try { final PackageManager packageManager = getPackageManager(); @@ -217,15 +241,91 @@ public class DeviceChooserActivity extends Activity { } } - private DeviceDiscoveryService getService() { - return DeviceDiscoveryService.sInstance; + private CompanionDeviceDiscoveryService getService() { + return CompanionDeviceDiscoveryService.sInstance; } - protected void onDeviceConfirmed(DeviceFilterPair selectedDevice) { + protected void onDeviceConfirmed(DeviceFilterPair selectedDevice) { + Log.i(LOG_TAG, "onDeviceConfirmed(selectedDevice = " + selectedDevice + ")"); getService().onDeviceSelected( getCallingPackage(), getDeviceMacAddress(selectedDevice.device)); setResult(RESULT_OK, new Intent().putExtra(CompanionDeviceManager.EXTRA_DEVICE, selectedDevice.device)); finish(); } + + class DevicesAdapter extends BaseAdapter { + private final Drawable mBluetoothIcon = icon(android.R.drawable.stat_sys_data_bluetooth); + private final Drawable mWifiIcon = icon(com.android.internal.R.drawable.ic_wifi_signal_3); + + private SparseArray<Integer> mColors = new SparseArray(); + + private Drawable icon(int drawableRes) { + Drawable icon = getResources().getDrawable(drawableRes, null); + icon.setTint(Color.DKGRAY); + return icon; + } + + @Override + public View getView( + int position, + @Nullable View convertView, + @NonNull ViewGroup parent) { + TextView view = convertView instanceof TextView + ? (TextView) convertView + : newView(); + bind(view, getItem(position)); + return view; + } + + private void bind(TextView textView, DeviceFilterPair device) { + textView.setText(device.getDisplayName()); + textView.setBackgroundColor( + device.equals(getService().mSelectedDevice) + ? getColor(android.R.attr.colorControlHighlight) + : Color.TRANSPARENT); + textView.setCompoundDrawablesWithIntrinsicBounds( + device.device instanceof android.net.wifi.ScanResult + ? mWifiIcon + : mBluetoothIcon, + null, null, null); + textView.getCompoundDrawables()[0].setTint(getColor(android.R.attr.colorForeground)); + } + + private TextView newView() { + final TextView textView = new TextView(CompanionDeviceActivity.this); + textView.setTextColor(getColor(android.R.attr.colorForeground)); + final int padding = CompanionDeviceActivity.getPadding(getResources()); + textView.setPadding(padding, padding, padding, padding); + textView.setCompoundDrawablePadding(padding); + return textView; + } + + private int getColor(int colorAttr) { + if (mColors.contains(colorAttr)) { + return mColors.get(colorAttr); + } + TypedValue typedValue = new TypedValue(); + TypedArray a = obtainStyledAttributes(typedValue.data, new int[] { colorAttr }); + int result = a.getColor(0, 0); + a.recycle(); + mColors.put(colorAttr, result); + return result; + } + + @Override + public int getCount() { + return getService().mDevicesFound.size(); + } + + @Override + public DeviceFilterPair getItem(int position) { + return getService().mDevicesFound.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + } } diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java index 67d4b41f164c..e620dfbafa08 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java @@ -50,9 +50,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.graphics.drawable.Drawable; import android.net.wifi.WifiManager; import android.os.Handler; import android.os.IBinder; @@ -60,12 +57,6 @@ import android.os.Parcelable; import android.os.RemoteException; import android.text.TextUtils; import android.util.Log; -import android.util.SparseArray; -import android.util.TypedValue; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.TextView; import com.android.internal.infra.AndroidFuture; import com.android.internal.util.ArrayUtils; @@ -76,14 +67,14 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; -public class DeviceDiscoveryService extends Service { +public class CompanionDeviceDiscoveryService extends Service { private static final boolean DEBUG = false; - private static final String LOG_TAG = "DeviceDiscoveryService"; + private static final String LOG_TAG = CompanionDeviceDiscoveryService.class.getSimpleName(); private static final long SCAN_TIMEOUT = 20000; - static DeviceDiscoveryService sInstance; + static CompanionDeviceDiscoveryService sInstance; private BluetoothManager mBluetoothManager; private BluetoothAdapter mBluetoothAdapter; @@ -102,12 +93,12 @@ public class DeviceDiscoveryService extends Service { AssociationRequest mRequest; List<DeviceFilterPair> mDevicesFound; DeviceFilterPair mSelectedDevice; - DevicesAdapter mDevicesAdapter; IFindDeviceCallback mFindCallback; AndroidFuture<Association> mServiceCallback; boolean mIsScanning = false; - @Nullable DeviceChooserActivity mActivity = null; + @Nullable + CompanionDeviceActivity mActivity = null; private final ICompanionDeviceDiscoveryService mBinder = new ICompanionDeviceDiscoveryService.Stub() { @@ -125,7 +116,8 @@ public class DeviceDiscoveryService extends Service { mFindCallback = findCallback; mServiceCallback = serviceCallback; Handler.getMain().sendMessage(obtainMessage( - DeviceDiscoveryService::startDiscovery, DeviceDiscoveryService.this, request)); + CompanionDeviceDiscoveryService::startDiscovery, + CompanionDeviceDiscoveryService.this, request)); } }; @@ -151,7 +143,6 @@ public class DeviceDiscoveryService extends Service { mWifiManager = getSystemService(WifiManager.class); mDevicesFound = new ArrayList<>(); - mDevicesAdapter = new DevicesAdapter(); sInstance = this; } @@ -165,7 +156,8 @@ public class DeviceDiscoveryService extends Service { mWifiFilters = CollectionUtils.filter(mFilters, WifiDeviceFilter.class); mBluetoothFilters = CollectionUtils.filter(mFilters, BluetoothDeviceFilter.class); mBLEFilters = CollectionUtils.filter(mFilters, BluetoothLeDeviceFilter.class); - mBLEScanFilters = CollectionUtils.map(mBLEFilters, BluetoothLeDeviceFilter::getScanFilter); + mBLEScanFilters + = CollectionUtils.map(mBLEFilters, BluetoothLeDeviceFilter::getScanFilter); reset(); } else if (DEBUG) Log.i(LOG_TAG, "startDiscovery: duplicate request: " + request); @@ -223,7 +215,7 @@ public class DeviceDiscoveryService extends Service { } mIsScanning = true; Handler.getMain().sendMessageDelayed( - obtainMessage(DeviceDiscoveryService::stopScan, this), + obtainMessage(CompanionDeviceDiscoveryService::stopScan, this), SCAN_TIMEOUT); } @@ -237,7 +229,7 @@ public class DeviceDiscoveryService extends Service { stopScan(); mDevicesFound.clear(); mSelectedDevice = null; - mDevicesAdapter.notifyDataSetChanged(); + CompanionDeviceActivity.notifyDevicesChanged(); } @Override @@ -252,7 +244,7 @@ public class DeviceDiscoveryService extends Service { if (!mIsScanning) return; mIsScanning = false; - DeviceChooserActivity activity = mActivity; + CompanionDeviceActivity activity = mActivity; if (activity != null) { if (activity.mDeviceListView != null) { activity.mDeviceListView.removeFooterView(activity.mLoadingIndicator); @@ -276,7 +268,7 @@ public class DeviceDiscoveryService extends Service { if (device == null) return; Handler.getMain().sendMessage(obtainMessage( - DeviceDiscoveryService::onDeviceFoundMainThread, this, device)); + CompanionDeviceDiscoveryService::onDeviceFoundMainThread, this, device)); } @MainThread @@ -292,7 +284,7 @@ public class DeviceDiscoveryService extends Service { onReadyToShowUI(); } mDevicesFound.add(device); - mDevicesAdapter.notifyDataSetChanged(); + CompanionDeviceActivity.notifyDevicesChanged(); } //TODO also, on timeout -> call onFailure @@ -300,7 +292,7 @@ public class DeviceDiscoveryService extends Service { try { mFindCallback.onSuccess(PendingIntent.getActivity( this, 0, - new Intent(this, DeviceChooserActivity.class), + new Intent(this, CompanionDeviceActivity.class), PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE)); } catch (RemoteException e) { @@ -311,13 +303,13 @@ public class DeviceDiscoveryService extends Service { private void onDeviceLost(@Nullable DeviceFilterPair device) { Log.i(LOG_TAG, "Lost device " + device.getDisplayName()); Handler.getMain().sendMessage(obtainMessage( - DeviceDiscoveryService::onDeviceLostMainThread, this, device)); + CompanionDeviceDiscoveryService::onDeviceLostMainThread, this, device)); } @MainThread void onDeviceLostMainThread(@Nullable DeviceFilterPair device) { mDevicesFound.remove(device); - mDevicesAdapter.notifyDataSetChanged(); + CompanionDeviceActivity.notifyDevicesChanged(); } void onDeviceSelected(String callingPackage, String deviceAddress) { @@ -331,81 +323,6 @@ public class DeviceDiscoveryService extends Service { mServiceCallback.cancel(true); } - class DevicesAdapter extends BaseAdapter { - private Drawable BLUETOOTH_ICON = icon(android.R.drawable.stat_sys_data_bluetooth); - private Drawable WIFI_ICON = icon(com.android.internal.R.drawable.ic_wifi_signal_3); - - private SparseArray<Integer> mColors = new SparseArray(); - - private Drawable icon(int drawableRes) { - Drawable icon = getResources().getDrawable(drawableRes, null); - icon.setTint(Color.DKGRAY); - return icon; - } - - @Override - public View getView( - int position, - @Nullable View convertView, - @NonNull ViewGroup parent) { - TextView view = convertView instanceof TextView - ? (TextView) convertView - : newView(); - bind(view, getItem(position)); - return view; - } - - private void bind(TextView textView, DeviceFilterPair device) { - textView.setText(device.getDisplayName()); - textView.setBackgroundColor( - device.equals(mSelectedDevice) - ? getColor(android.R.attr.colorControlHighlight) - : Color.TRANSPARENT); - textView.setCompoundDrawablesWithIntrinsicBounds( - device.device instanceof android.net.wifi.ScanResult - ? WIFI_ICON - : BLUETOOTH_ICON, - null, null, null); - textView.getCompoundDrawables()[0].setTint(getColor(android.R.attr.colorForeground)); - } - - private TextView newView() { - final TextView textView = new TextView(DeviceDiscoveryService.this); - textView.setTextColor(getColor(android.R.attr.colorForeground)); - final int padding = DeviceChooserActivity.getPadding(getResources()); - textView.setPadding(padding, padding, padding, padding); - textView.setCompoundDrawablePadding(padding); - return textView; - } - - private int getColor(int colorAttr) { - if (mColors.contains(colorAttr)) { - return mColors.get(colorAttr); - } - TypedValue typedValue = new TypedValue(); - TypedArray a = obtainStyledAttributes(typedValue.data, new int[] { colorAttr }); - int result = a.getColor(0, 0); - a.recycle(); - mColors.put(colorAttr, result); - return result; - } - - @Override - public int getCount() { - return mDevicesFound.size(); - } - - @Override - public DeviceFilterPair getItem(int position) { - return mDevicesFound.get(position); - } - - @Override - public long getItemId(int position) { - return position; - } - } - /** * A pair of device and a filter that matched this device if any. * diff --git a/packages/Connectivity/framework/src/android/net/CaptivePortalData.java b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java index f4b46e9f11ed..eafda4d2d694 100644 --- a/packages/Connectivity/framework/src/android/net/CaptivePortalData.java +++ b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java @@ -44,7 +44,7 @@ public final class CaptivePortalData implements Parcelable { private final boolean mCaptive; private final String mVenueFriendlyName; private final int mVenueInfoUrlSource; - private final int mTermsAndConditionsSource; + private final int mUserPortalUrlSource; /** @hide */ @Retention(RetentionPolicy.SOURCE) @@ -65,7 +65,7 @@ public final class CaptivePortalData implements Parcelable { private CaptivePortalData(long refreshTimeMillis, Uri userPortalUrl, Uri venueInfoUrl, boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive, - String venueFriendlyName, int venueInfoUrlSource, int termsAndConditionsSource) { + String venueFriendlyName, int venueInfoUrlSource, int userPortalUrlSource) { mRefreshTimeMillis = refreshTimeMillis; mUserPortalUrl = userPortalUrl; mVenueInfoUrl = venueInfoUrl; @@ -75,7 +75,7 @@ public final class CaptivePortalData implements Parcelable { mCaptive = captive; mVenueFriendlyName = venueFriendlyName; mVenueInfoUrlSource = venueInfoUrlSource; - mTermsAndConditionsSource = termsAndConditionsSource; + mUserPortalUrlSource = userPortalUrlSource; } private CaptivePortalData(Parcel p) { @@ -100,7 +100,7 @@ public final class CaptivePortalData implements Parcelable { dest.writeBoolean(mCaptive); dest.writeString(mVenueFriendlyName); dest.writeInt(mVenueInfoUrlSource); - dest.writeInt(mTermsAndConditionsSource); + dest.writeInt(mUserPortalUrlSource); } /** @@ -130,7 +130,7 @@ public final class CaptivePortalData implements Parcelable { public Builder(@Nullable CaptivePortalData data) { if (data == null) return; setRefreshTime(data.mRefreshTimeMillis) - .setUserPortalUrl(data.mUserPortalUrl, data.mTermsAndConditionsSource) + .setUserPortalUrl(data.mUserPortalUrl, data.mUserPortalUrlSource) .setVenueInfoUrl(data.mVenueInfoUrl, data.mVenueInfoUrlSource) .setSessionExtendable(data.mIsSessionExtendable) .setBytesRemaining(data.mByteLimit) @@ -314,7 +314,7 @@ public final class CaptivePortalData implements Parcelable { * @return The source that the user portal URL was obtained from */ public @CaptivePortalDataSource int getUserPortalUrlSource() { - return mTermsAndConditionsSource; + return mUserPortalUrlSource; } /** @@ -342,7 +342,7 @@ public final class CaptivePortalData implements Parcelable { public int hashCode() { return Objects.hash(mRefreshTimeMillis, mUserPortalUrl, mVenueInfoUrl, mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive, mVenueFriendlyName, - mVenueInfoUrlSource, mTermsAndConditionsSource); + mVenueInfoUrlSource, mUserPortalUrlSource); } @Override @@ -358,7 +358,7 @@ public final class CaptivePortalData implements Parcelable { && mCaptive == other.mCaptive && Objects.equals(mVenueFriendlyName, other.mVenueFriendlyName) && mVenueInfoUrlSource == other.mVenueInfoUrlSource - && mTermsAndConditionsSource == other.mTermsAndConditionsSource; + && mUserPortalUrlSource == other.mUserPortalUrlSource; } @Override @@ -373,7 +373,7 @@ public final class CaptivePortalData implements Parcelable { + ", captive: " + mCaptive + ", venueFriendlyName: " + mVenueFriendlyName + ", venueInfoUrlSource: " + mVenueInfoUrlSource - + ", termsAndConditionsSource: " + mTermsAndConditionsSource + + ", userPortalUrlSource: " + mUserPortalUrlSource + "}"; } } diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java index 4ddae533bb80..e7ab0a1c7ac8 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java @@ -1067,58 +1067,6 @@ public class ConnectivityManager { } /** - * Calls VpnManager#isAlwaysOnVpnPackageSupportedForUser. - * @deprecated TODO: remove when callers have migrated to VpnManager. - * @hide - */ - @Deprecated - public boolean isAlwaysOnVpnPackageSupportedForUser(int userId, @Nullable String vpnPackage) { - return getVpnManager().isAlwaysOnVpnPackageSupportedForUser(userId, vpnPackage); - } - - /** - * Calls VpnManager#setAlwaysOnVpnPackageForUser. - * @deprecated TODO: remove when callers have migrated to VpnManager. - * @hide - */ - @Deprecated - public boolean setAlwaysOnVpnPackageForUser(int userId, @Nullable String vpnPackage, - boolean lockdownEnabled, @Nullable List<String> lockdownAllowlist) { - return getVpnManager().setAlwaysOnVpnPackageForUser(userId, vpnPackage, lockdownEnabled, - lockdownAllowlist); - } - - /** - * Calls VpnManager#getAlwaysOnVpnPackageForUser. - * @deprecated TODO: remove when callers have migrated to VpnManager. - * @hide - */ - @Deprecated - public String getAlwaysOnVpnPackageForUser(int userId) { - return getVpnManager().getAlwaysOnVpnPackageForUser(userId); - } - - /** - * Calls VpnManager#isVpnLockdownEnabled. - * @deprecated TODO: remove when callers have migrated to VpnManager. - * @hide - */ - @Deprecated - public boolean isVpnLockdownEnabled(int userId) { - return getVpnManager().isVpnLockdownEnabled(userId); - } - - /** - * Calls VpnManager#getVpnLockdownAllowlist. - * @deprecated TODO: remove when callers have migrated to VpnManager. - * @hide - */ - @Deprecated - public List<String> getVpnLockdownAllowlist(int userId) { - return getVpnManager().getVpnLockdownAllowlist(userId); - } - - /** * Adds or removes a requirement for given UID ranges to use the VPN. * * If set to {@code true}, informs the system that the UIDs in the specified ranges must not @@ -3153,16 +3101,6 @@ public class ConnectivityManager { } /** - * Calls VpnManager#updateLockdownVpn. - * @deprecated TODO: remove when callers have migrated to VpnManager. - * @hide - */ - @Deprecated - public boolean updateLockdownVpn() { - return getVpnManager().updateLockdownVpn(); - } - - /** * Set sign in error notification to visible or invisible * * @hide @@ -4523,8 +4461,6 @@ public class ConnectivityManager { try { mService.factoryReset(); mTetheringManager.stopAllTethering(); - // TODO: Migrate callers to VpnManager#factoryReset. - getVpnManager().factoryReset(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -4818,15 +4754,6 @@ public class ConnectivityManager { return new TestNetworkManager(ITestNetworkManager.Stub.asInterface(tnBinder)); } - /** - * Temporary hack to shim calls from ConnectivityManager to VpnManager. We cannot store a - * private final mVpnManager because ConnectivityManager is initialized before VpnManager. - * @hide TODO: remove. - */ - public VpnManager getVpnManager() { - return mContext.getSystemService(VpnManager.class); - } - /** @hide */ public ConnectivityDiagnosticsManager createDiagnosticsManager() { return new ConnectivityDiagnosticsManager(mContext, mService); diff --git a/packages/Connectivity/framework/src/android/net/IpPrefix.java b/packages/Connectivity/framework/src/android/net/IpPrefix.java index bcb65fab8571..d2ee7d13b05f 100644 --- a/packages/Connectivity/framework/src/android/net/IpPrefix.java +++ b/packages/Connectivity/framework/src/android/net/IpPrefix.java @@ -24,6 +24,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.Pair; +import com.android.net.module.util.NetUtils; + import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; @@ -59,7 +61,7 @@ public final class IpPrefix implements Parcelable { throw new IllegalArgumentException( "IpPrefix has " + address.length + " bytes which is neither 4 nor 16"); } - NetworkUtils.maskRawAddress(address, prefixLength); + NetUtils.maskRawAddress(address, prefixLength); } /** @@ -190,7 +192,7 @@ public final class IpPrefix implements Parcelable { if (addrBytes == null || addrBytes.length != this.address.length) { return false; } - NetworkUtils.maskRawAddress(addrBytes, prefixLength); + NetUtils.maskRawAddress(addrBytes, prefixLength); return Arrays.equals(this.address, addrBytes); } @@ -204,7 +206,7 @@ public final class IpPrefix implements Parcelable { public boolean containsPrefix(@NonNull IpPrefix otherPrefix) { if (otherPrefix.getPrefixLength() < prefixLength) return false; final byte[] otherAddress = otherPrefix.getRawAddress(); - NetworkUtils.maskRawAddress(otherAddress, prefixLength); + NetUtils.maskRawAddress(otherAddress, prefixLength); return Arrays.equals(otherAddress, address); } diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java index 26d14cbfaa95..cd76f409b093 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java +++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java @@ -205,6 +205,7 @@ public final class NetworkCapabilities implements Parcelable { NET_CAPABILITY_OEM_PRIVATE, NET_CAPABILITY_VEHICLE_INTERNAL, NET_CAPABILITY_NOT_VCN_MANAGED, + NET_CAPABILITY_ENTERPRISE, }) public @interface NetCapability { } @@ -415,8 +416,17 @@ public final class NetworkCapabilities implements Parcelable { @SystemApi public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; + /** + * Indicates that this network is intended for enterprise use. + * <p> + * 5G URSP rules may indicate that all data should use a connection dedicated for enterprise + * use. If the enterprise capability is requested, all enterprise traffic will be routed over + * the connection with this capability. + */ + public static final int NET_CAPABILITY_ENTERPRISE = 29; + private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS; - private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_VCN_MANAGED; + private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_ENTERPRISE; /** * Network capabilities that are expected to be mutable, i.e., can change while a particular @@ -474,7 +484,8 @@ public final class NetworkCapabilities implements Parcelable { | (1 << NET_CAPABILITY_MCX) | (1 << NET_CAPABILITY_RCS) | (1 << NET_CAPABILITY_VEHICLE_INTERNAL) - | (1 << NET_CAPABILITY_XCAP); + | (1 << NET_CAPABILITY_XCAP) + | (1 << NET_CAPABILITY_ENTERPRISE); /** * Capabilities that force network to be restricted. @@ -2028,8 +2039,9 @@ public final class NetworkCapabilities implements Parcelable { case NET_CAPABILITY_PARTIAL_CONNECTIVITY: return "PARTIAL_CONNECTIVITY"; case NET_CAPABILITY_TEMPORARILY_NOT_METERED: return "TEMPORARILY_NOT_METERED"; case NET_CAPABILITY_OEM_PRIVATE: return "OEM_PRIVATE"; - case NET_CAPABILITY_VEHICLE_INTERNAL: return "NET_CAPABILITY_VEHICLE_INTERNAL"; + case NET_CAPABILITY_VEHICLE_INTERNAL: return "VEHICLE_INTERNAL"; case NET_CAPABILITY_NOT_VCN_MANAGED: return "NOT_VCN_MANAGED"; + case NET_CAPABILITY_ENTERPRISE: return "ENTERPRISE"; default: return Integer.toString(capability); } } diff --git a/packages/Connectivity/framework/src/android/net/NetworkUtils.java b/packages/Connectivity/framework/src/android/net/NetworkUtils.java index 8be4af7b1396..9ccb04a44af4 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkUtils.java +++ b/packages/Connectivity/framework/src/android/net/NetworkUtils.java @@ -29,7 +29,6 @@ import java.math.BigInteger; import java.net.Inet4Address; import java.net.InetAddress; import java.net.SocketException; -import java.net.UnknownHostException; import java.util.Locale; import java.util.TreeSet; @@ -232,46 +231,6 @@ public class NetworkUtils { } /** - * Masks a raw IP address byte array with the specified prefix length. - */ - public static void maskRawAddress(byte[] array, int prefixLength) { - if (prefixLength < 0 || prefixLength > array.length * 8) { - throw new RuntimeException("IP address with " + array.length + - " bytes has invalid prefix length " + prefixLength); - } - - int offset = prefixLength / 8; - int remainder = prefixLength % 8; - byte mask = (byte)(0xFF << (8 - remainder)); - - if (offset < array.length) array[offset] = (byte)(array[offset] & mask); - - offset++; - - for (; offset < array.length; offset++) { - array[offset] = 0; - } - } - - /** - * Get InetAddress masked with prefixLength. Will never return null. - * @param address the IP address to mask with - * @param prefixLength the prefixLength used to mask the IP - */ - public static InetAddress getNetworkPart(InetAddress address, int prefixLength) { - byte[] array = address.getAddress(); - maskRawAddress(array, prefixLength); - - InetAddress netPart = null; - try { - netPart = InetAddress.getByAddress(array); - } catch (UnknownHostException e) { - throw new RuntimeException("getNetworkPart error - " + e.toString()); - } - return netPart; - } - - /** * Returns the implicit netmask of an IPv4 address, as was the custom before 1993. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) diff --git a/packages/DynamicSystemInstallationService/res/values-eu/strings.xml b/packages/DynamicSystemInstallationService/res/values-eu/strings.xml index 6576edd386d6..b0d45e1d061a 100644 --- a/packages/DynamicSystemInstallationService/res/values-eu/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-eu/strings.xml @@ -5,7 +5,7 @@ <string name="notification_install_completed" msgid="6252047868415172643">"Prest dago sistema dinamikoa. Erabiltzen hasteko, berrabiarazi gailua."</string> <string name="notification_install_inprogress" msgid="7383334330065065017">"Instalatzen"</string> <string name="notification_install_failed" msgid="4066039210317521404">"Ezin izan da instalatu"</string> - <string name="notification_image_validation_failed" msgid="2720357826403917016">"Ezin izan da balidatu irudia. Utzi bertan behera instalazioa."</string> + <string name="notification_image_validation_failed" msgid="2720357826403917016">"Ezin izan da baliozkotu irudia. Utzi bertan behera instalazioa."</string> <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Sistema dinamiko bat abian da. Berrabiarazi Android-en jatorrizko bertsioa erabiltzeko."</string> <string name="notification_action_cancel" msgid="5929299408545961077">"Utzi"</string> <string name="notification_action_discard" msgid="1817481003134947493">"Baztertu"</string> diff --git a/packages/InputDevices/OWNERS b/packages/InputDevices/OWNERS index 0313a40f7270..f0d6db88bcc5 100644 --- a/packages/InputDevices/OWNERS +++ b/packages/InputDevices/OWNERS @@ -1,2 +1 @@ -michaelwr@google.com -svv@google.com +include /services/core/java/com/android/server/input/OWNERS diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java index 8b4db92910f5..2946db3cdcf0 100644 --- a/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java +++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java @@ -27,9 +27,11 @@ public class LocalTransportParameters extends KeyValueSettingObserver { private static final String SETTING = Settings.Secure.BACKUP_LOCAL_TRANSPORT_PARAMETERS; private static final String KEY_FAKE_ENCRYPTION_FLAG = "fake_encryption_flag"; private static final String KEY_NON_INCREMENTAL_ONLY = "non_incremental_only"; + private static final String KEY_IS_DEVICE_TRANSFER = "is_device_transfer"; private boolean mFakeEncryptionFlag; private boolean mIsNonIncrementalOnly; + private boolean mIsDeviceTransfer; public LocalTransportParameters(Handler handler, ContentResolver resolver) { super(handler, resolver, Settings.Secure.getUriFor(SETTING)); @@ -43,6 +45,10 @@ public class LocalTransportParameters extends KeyValueSettingObserver { return mIsNonIncrementalOnly; } + boolean isDeviceTransfer() { + return mIsDeviceTransfer; + } + public String getSettingValue(ContentResolver resolver) { return Settings.Secure.getString(resolver, SETTING); } @@ -50,5 +56,6 @@ public class LocalTransportParameters extends KeyValueSettingObserver { public void update(KeyValueListParser parser) { mFakeEncryptionFlag = parser.getBoolean(KEY_FAKE_ENCRYPTION_FLAG, false); mIsNonIncrementalOnly = parser.getBoolean(KEY_NON_INCREMENTAL_ONLY, false); + mIsDeviceTransfer = parser.getBoolean(KEY_IS_DEVICE_TRANSFER, false); } } diff --git a/packages/SettingsLib/BannerMessagePreference/Android.bp b/packages/SettingsLib/BannerMessagePreference/Android.bp index 095975afa13a..82e837bcd3ac 100644 --- a/packages/SettingsLib/BannerMessagePreference/Android.bp +++ b/packages/SettingsLib/BannerMessagePreference/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_library { name: "SettingsLibBannerMessagePreference", diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp index c23ff05f34ab..ed49bf4d5385 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_library { name: "SettingsLibCollapsingToolbarBaseActivity", diff --git a/packages/SettingsLib/EmergencyNumber/Android.bp b/packages/SettingsLib/EmergencyNumber/Android.bp index 3c41f7834d6c..25b4905c438f 100644 --- a/packages/SettingsLib/EmergencyNumber/Android.bp +++ b/packages/SettingsLib/EmergencyNumber/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_library { name: "SettingsLibEmergencyNumber", diff --git a/packages/SettingsLib/FooterPreference/Android.bp b/packages/SettingsLib/FooterPreference/Android.bp index 1af967fef717..11f39e7bb210 100644 --- a/packages/SettingsLib/FooterPreference/Android.bp +++ b/packages/SettingsLib/FooterPreference/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_library { name: "SettingsLibFooterPreference", diff --git a/packages/SettingsLib/MainSwitchPreference/Android.bp b/packages/SettingsLib/MainSwitchPreference/Android.bp index 1dc18f5cc4d2..1feec21e24e4 100644 --- a/packages/SettingsLib/MainSwitchPreference/Android.bp +++ b/packages/SettingsLib/MainSwitchPreference/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_library { name: "SettingsLibMainSwitchPreference", diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml index 52779bcabf00..85c01c5732ca 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml @@ -19,6 +19,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" android:layout_width="match_parent" + android:background="?android:attr/colorBackground" android:orientation="vertical"> <LinearLayout diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java index 74b65780ffc2..1c9298ed6085 100644 --- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java +++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java @@ -17,6 +17,7 @@ package com.android.settingslib.widget; import android.content.Context; +import android.content.res.TypedArray; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; @@ -29,6 +30,7 @@ import android.widget.Switch; import android.widget.TextView; import androidx.annotation.VisibleForTesting; +import androidx.core.content.res.TypedArrayUtils; import com.android.settingslib.RestrictedLockUtils; @@ -88,6 +90,17 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec }); setChecked(mSwitch.isChecked()); + + if (attrs != null) { + final TypedArray a = context.obtainStyledAttributes(attrs, + androidx.preference.R.styleable.Preference, 0 /*defStyleAttr*/, + 0 /*defStyleRes*/); + final CharSequence title = TypedArrayUtils.getText(a, + androidx.preference.R.styleable.Preference_title, + androidx.preference.R.styleable.Preference_android_title); + setTitle(title); + a.recycle(); + } } @Override @@ -126,7 +139,7 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec /** * Set the title text */ - public void setTitle(String text) { + public void setTitle(CharSequence text) { if (mTextView != null) { mTextView.setText(text); } diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java index 274bf8df2222..35afec38dd3d 100644 --- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java +++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java @@ -18,7 +18,6 @@ package com.android.settingslib.widget; import android.content.Context; import android.content.res.TypedArray; -import android.text.TextUtils; import android.util.AttributeSet; import androidx.core.content.res.TypedArrayUtils; @@ -40,7 +39,7 @@ public class MainSwitchPreference extends TwoStatePreference { private final List<OnMainSwitchChangeListener> mSwitchChangeListeners = new ArrayList<>(); private MainSwitchBar mMainSwitchBar; - private String mTitle; + private CharSequence mTitle; private RestrictedLockUtils.EnforcedAdmin mEnforcedAdmin; @@ -81,24 +80,28 @@ public class MainSwitchPreference extends TwoStatePreference { setLayoutResource(R.layout.main_switch_layout); if (attrs != null) { - TypedArray a = context.obtainStyledAttributes(attrs, + final TypedArray a = context.obtainStyledAttributes(attrs, androidx.preference.R.styleable.Preference, 0 /*defStyleAttr*/, 0 /*defStyleRes*/); final CharSequence title = TypedArrayUtils.getText(a, androidx.preference.R.styleable.Preference_title, androidx.preference.R.styleable.Preference_android_title); - if (!TextUtils.isEmpty(title)) { - setTitle(title.toString()); - } + setTitle(title); a.recycle(); } } - /** - * Set the preference title text - */ - public void setTitle(String text) { - mTitle = text; + @Override + public void setChecked(boolean checked) { + super.setChecked(checked); + if (mMainSwitchBar != null) { + mMainSwitchBar.setChecked(checked); + } + } + + @Override + public void setTitle(CharSequence title) { + mTitle = title; if (mMainSwitchBar != null) { mMainSwitchBar.setTitle(mTitle); } diff --git a/packages/SettingsLib/TopIntroPreference/Android.bp b/packages/SettingsLib/TopIntroPreference/Android.bp index 03becbd23226..957728120c4d 100644 --- a/packages/SettingsLib/TopIntroPreference/Android.bp +++ b/packages/SettingsLib/TopIntroPreference/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_library { name: "SettingsLibTopIntroPreference", diff --git a/packages/SettingsLib/UsageProgressBarPreference/Android.bp b/packages/SettingsLib/UsageProgressBarPreference/Android.bp index 3331550d0535..ad6e7ab9f564 100644 --- a/packages/SettingsLib/UsageProgressBarPreference/Android.bp +++ b/packages/SettingsLib/UsageProgressBarPreference/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_library { name: "SettingsLibUsageProgressBarPreference", diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 4c80b91f300d..5e2d21b2e188 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -851,11 +851,12 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> if (BluetoothUuid.containsAnyUuid(uuids, PbapServerProfile.PBAB_CLIENT_UUIDS)) { // The pairing dialog now warns of phone-book access for paired devices. // No separate prompt is displayed after pairing. + final BluetoothClass bluetoothClass = mDevice.getBluetoothClass(); if (mDevice.getPhonebookAccessPermission() == BluetoothDevice.ACCESS_UNKNOWN) { - if (mDevice.getBluetoothClass().getDeviceClass() - == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE || - mDevice.getBluetoothClass().getDeviceClass() - == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET) { + if (bluetoothClass != null && (bluetoothClass.getDeviceClass() + == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE + || bluetoothClass.getDeviceClass() + == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET)) { EventLog.writeEvent(0x534e4554, "138529441", -1, ""); } mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED); diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index 32419f49d8c9..1f3e0e9fe038 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -205,7 +205,6 @@ public class LocalMediaManager implements BluetoothCallback { void dispatchDeviceListUpdate() { final List<MediaDevice> mediaDevices = new ArrayList<>(mMediaDevices); - Collections.sort(mediaDevices, COMPARATOR); for (DeviceCallback callback : getCallbacks()) { callback.onDeviceListUpdate(mediaDevices); } @@ -472,6 +471,7 @@ public class LocalMediaManager implements BluetoothCallback { synchronized (mMediaDevicesLock) { mMediaDevices.clear(); mMediaDevices.addAll(devices); + Collections.sort(devices, COMPARATOR); // Add disconnected bluetooth devices only when phone output device is available. for (MediaDevice device : devices) { final int type = device.getDeviceType(); diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java index 647fd2acf7c8..552fa11a42b7 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java @@ -39,8 +39,7 @@ public class MediaOutputConstants { /** * A string extra specifying a media package name. */ - public static final String EXTRA_PACKAGE_NAME = - "com.android.settings.panel.extra.PACKAGE_NAME"; + public static final String EXTRA_PACKAGE_NAME = "package_name"; /** * An intent action to launch media output dialog. diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java index 53ff1a10b6ff..53a99ab8cbe4 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java @@ -27,12 +27,14 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.media.AudioManager; import com.android.settingslib.R; +import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter; import org.junit.Before; import org.junit.Test; @@ -41,8 +43,11 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; @RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowBluetoothAdapter.class}) public class CachedBluetoothDeviceTest { private static final String DEVICE_NAME = "TestName"; private static final String DEVICE_ALIAS = "TestAlias"; @@ -72,12 +77,14 @@ public class CachedBluetoothDeviceTest { private AudioManager mAudioManager; private Context mContext; private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; + private ShadowBluetoothAdapter mShadowBluetoothAdapter; @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; mAudioManager = mContext.getSystemService(AudioManager.class); + mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); when(mDevice.getAddress()).thenReturn(DEVICE_ADDRESS); when(mHfpProfile.isProfileReady()).thenReturn(true); when(mA2dpProfile.isProfileReady()).thenReturn(true); @@ -937,4 +944,17 @@ public class CachedBluetoothDeviceTest { assertThat(mCachedDevice.getConnectionSummary()).isEqualTo( mContext.getString(R.string.profile_connect_timeout_subtext)); } + + @Test + public void onUuidChanged_bluetoothClassIsNull_shouldNotCrash() { + mShadowBluetoothAdapter.setUuids(PbapServerProfile.PBAB_CLIENT_UUIDS); + when(mDevice.getUuids()).thenReturn(PbapServerProfile.PBAB_CLIENT_UUIDS); + when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); + when(mDevice.getPhonebookAccessPermission()).thenReturn(BluetoothDevice.ACCESS_UNKNOWN); + when(mDevice.getBluetoothClass()).thenReturn(null); + + mCachedDevice.onUuidChanged(); + + // Should not crash + } } diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java index b265d46058be..3b7fbc73522f 100644 --- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java +++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java @@ -24,6 +24,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.content.Context; +import android.os.ParcelUuid; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; @@ -36,6 +37,7 @@ public class ShadowBluetoothAdapter extends org.robolectric.shadows.ShadowBlueto private List<Integer> mSupportedProfiles; private List<BluetoothDevice> mMostRecentlyConnectedDevices; private BluetoothProfile.ServiceListener mServiceListener; + private ParcelUuid[] mParcelUuids; @Implementation protected boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener, @@ -87,4 +89,13 @@ public class ShadowBluetoothAdapter extends org.robolectric.shadows.ShadowBlueto } return true; } + + @Implementation + protected ParcelUuid[] getUuids() { + return mParcelUuids; + } + + public void setUuids(ParcelUuid[] uuids) { + mParcelUuids = uuids; + } } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java index ad6a5312f156..66165b6d1ff2 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java @@ -147,10 +147,5 @@ public class GlobalSettingsValidators { 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/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index d715832dfca0..664fd4ac2fcb 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -283,7 +283,6 @@ public class SettingsBackupTest { 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, @@ -299,7 +298,6 @@ public class SettingsBackupTest { Settings.Global.GNSS_SATELLITE_BLOCKLIST, Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS, Settings.Global.HDMI_CEC_SWITCH_ENABLED, - Settings.Global.HDMI_CEC_VERSION, Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, Settings.Global.HDMI_CONTROL_ENABLED, diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 259484073162..7ced202c5e44 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -125,6 +125,8 @@ <uses-permission android:name="android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP" /> <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" /> <uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" /> + <uses-permission android:name="android.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS" /> + <uses-permission android:name="android.permission.CLEAR_FREEZE_PERIOD" /> <uses-permission android:name="android.permission.QUERY_USERS" /> <uses-permission android:name="android.permission.MODIFY_QUIET_MODE" /> <uses-permission android:name="android.permission.ACCESS_LOWPAN_STATE"/> diff --git a/packages/Shell/OWNERS b/packages/Shell/OWNERS index 34901f5830c4..6d738f8d4d43 100644 --- a/packages/Shell/OWNERS +++ b/packages/Shell/OWNERS @@ -9,3 +9,4 @@ yamasani@google.com toddke@google.com cbrubaker@google.com omakoto@google.com +michaelwr@google.com diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml index 71cdaf5c7091..79868093fb12 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="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" /> </FrameLayout> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml index c75ee51517d1..04e645bd0a32 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml @@ -41,14 +41,13 @@ android:layout_gravity="center"> <com.android.keyguard.KeyguardSecurityViewFlipper android:id="@+id/view_flipper" - android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_width="match_parent" + android:layout_height="match_parent" 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/values/config.xml b/packages/SystemUI/res-keyguard/values/config.xml index 6176f7c1dd0a..8d9d6ee68c67 100644 --- a/packages/SystemUI/res-keyguard/values/config.xml +++ b/packages/SystemUI/res-keyguard/values/config.xml @@ -22,5 +22,4 @@ <!-- 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/drawable/brightness_progress_drawable_thick.xml b/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml index d097472471b0..8efe0539207a 100644 --- a/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml +++ b/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml @@ -41,8 +41,10 @@ </item> <item android:id="@android:id/progress" android:gravity="center_vertical|fill_horizontal"> - <com.android.systemui.util.RoundedCornerProgressDrawable + <clip android:drawable="@drawable/brightness_progress_full_drawable" + android:clipOrientation="horizontal" + android:gravity="left" /> </item> </layer-list>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml index 41140a7a8c85..5bc2773dc657 100644 --- a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml +++ b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml @@ -26,10 +26,10 @@ </item> <item android:id="@+id/slider_icon" - android:gravity="center_vertical|right" + android:gravity="center_vertical|left" android:height="@dimen/rounded_slider_icon_size" android:width="@dimen/rounded_slider_icon_size" - android:right="@dimen/rounded_slider_icon_inset"> + android:left="@dimen/rounded_slider_icon_inset"> <com.android.systemui.util.AlphaTintDrawableWrapper android:drawable="@drawable/ic_brightness" android:tint="?android:attr/colorBackground" diff --git a/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml b/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml index 9f66581d8053..96e01934e670 100644 --- a/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml +++ b/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml @@ -36,8 +36,4 @@ </vector> </item> - <item> - <ripple android:color="@color/GM2_grey_600"></ripple> - </item> - </layer-list> diff --git a/packages/SystemUI/res/drawable/ic_open_in_new_window.xml b/packages/SystemUI/res/drawable/ic_open_in_new_window.xml index 659b02048c73..368c8dfb5023 100644 --- a/packages/SystemUI/res/drawable/ic_open_in_new_window.xml +++ b/packages/SystemUI/res/drawable/ic_open_in_new_window.xml @@ -50,28 +50,7 @@ android:strokeWidth="1" android:pathData="M 12.5 13.92 L 15.59 17 L 17 15.59 L 13.91 12.5 L 16.5 12.5 L 16.5 10.5 L 10.5 10.5 L 10.5 16.5 L 12.5 16.5 Z" /> </group> - <group - android:translateX="22.000000" - android:translateY="2.000000"> - <group - android:translateX="3.000000" - android:translateY="3.000000"> - <group - android:translateX="-3.000000" - android:translateY="-3.000000"> - <path - android:fillColor="#80868B" - android:fillType="evenOdd" - android:strokeWidth="1" - android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" /> - </group> - </group> - </group> </vector> </item> - <item> - <ripple android:color="@color/GM2_grey_600"></ripple> - </item> - </layer-list> diff --git a/packages/SystemUI/res/drawable/ic_phone_missed.xml b/packages/SystemUI/res/drawable/ic_phone_missed.xml new file mode 100644 index 000000000000..72e67d4a2ed0 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_phone_missed.xml @@ -0,0 +1,24 @@ +<!-- +Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M6.5,5.5L12,11l7,-7 -1,-1 -6,6 -4.5,-4.5L11,4.5L11,3L5,3v6h1.5L6.5,5.5zM23.71,16.67C20.66,13.78 16.54,12 12,12 7.46,12 3.34,13.78 0.29,16.67c-0.18,0.18 -0.29,0.43 -0.29,0.71s0.11,0.53 0.29,0.71l2.48,2.48c0.18,0.18 0.43,0.29 0.71,0.29 0.27,0 0.52,-0.11 0.7,-0.28 0.79,-0.74 1.69,-1.36 2.66,-1.85 0.33,-0.16 0.56,-0.5 0.56,-0.9v-3.1c1.45,-0.48 3,-0.73 4.6,-0.73 1.6,0 3.15,0.25 4.6,0.72v3.1c0,0.39 0.23,0.74 0.56,0.9 0.98,0.49 1.87,1.12 2.67,1.85 0.18,0.18 0.43,0.28 0.7,0.28 0.28,0 0.53,-0.11 0.71,-0.29l2.48,-2.48c0.18,-0.18 0.29,-0.43 0.29,-0.71s-0.12,-0.52 -0.3,-0.7z" /> +</vector> diff --git a/packages/SystemUI/res/drawable/people_space_content_background.xml b/packages/SystemUI/res/drawable/people_space_content_background.xml index 32314d29277e..30519aeddb45 100644 --- a/packages/SystemUI/res/drawable/people_space_content_background.xml +++ b/packages/SystemUI/res/drawable/people_space_content_background.xml @@ -15,6 +15,6 @@ ~ limitations under the License. --> <shape xmlns:android="http://schemas.android.com/apk/res/android" > - <solid android:color="?android:attr/colorControlHighlight" /> + <solid android:color="?android:attr/colorBackground" /> <corners android:radius="@dimen/people_space_image_radius" /> </shape> diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml index 73beefc9da83..d996cee4b39e 100644 --- a/packages/SystemUI/res/layout/media_output_dialog.xml +++ b/packages/SystemUI/res/layout/media_output_dialog.xml @@ -32,7 +32,8 @@ android:id="@+id/header_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:paddingEnd="@dimen/media_output_dialog_header_icon_padding"/> + android:paddingEnd="@dimen/media_output_dialog_header_icon_padding" + android:importantForAccessibility="no"/> <LinearLayout android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index 23f34251b812..4ca59f5082ad 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -37,12 +37,6 @@ android:layout_height="match_parent" android:layout_width="match_parent" /> - <ViewStub - android:id="@+id/keyguard_user_switcher_stub" - android:layout="@layout/keyguard_user_switcher" - android:layout_height="match_parent" - android:layout_width="match_parent" /> - <include layout="@layout/keyguard_status_view" android:visibility="gone" /> @@ -109,5 +103,11 @@ layout="@layout/keyguard_bottom_area" android:visibility="gone" /> + <ViewStub + android:id="@+id/keyguard_user_switcher_stub" + android:layout="@layout/keyguard_user_switcher" + android:layout_height="match_parent" + android:layout_width="match_parent" /> + <include layout="@layout/status_bar_expanded_plugin_frame"/> </com.android.systemui.statusbar.phone.NotificationPanelView> diff --git a/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml b/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml deleted file mode 100644 index b62018d7cb9e..000000000000 --- a/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml +++ /dev/null @@ -1,38 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ 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. - --> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:padding="12dp"> - - <FrameLayout - android:layout_width="34dp" - android:layout_height="24dp" - android:layout_gravity="center" - android:background="@drawable/tv_rect_shadow_rounded"> - - <ImageView - android:layout_width="13dp" - android:layout_height="13dp" - android:layout_gravity="center" - android:src="@drawable/tv_ic_mic_white"/> - - </FrameLayout> - -</LinearLayout> diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml b/packages/SystemUI/res/layout/tv_ongoing_privacy_chip.xml index e09bf7e37ed0..dff148b2c570 100644 --- a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml +++ b/packages/SystemUI/res/layout/tv_ongoing_privacy_chip.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2021 The Android Open Source Project + ~ 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. @@ -15,6 +15,21 @@ ~ limitations under the License. --> -<resources> - <bool name="can_use_one_handed_bouncer">true</bool> -</resources> + +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="12dp" + android:layout_gravity="center"> + + <LinearLayout + android:id="@+id/icons_container" + android:background="@drawable/tv_rect_shadow_rounded" + android:padding="@dimen/privacy_chip_icon_padding" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:orientation="horizontal"/> + +</FrameLayout> + diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml index 722f1480463d..b02d8b8cf7e4 100644 --- a/packages/SystemUI/res/values-television/config.xml +++ b/packages/SystemUI/res/values-television/config.xml @@ -28,6 +28,7 @@ <string-array name="config_systemUIServiceComponents" translatable="false"> <item>com.android.systemui.util.NotificationChannels</item> <item>com.android.systemui.volume.VolumeUI</item> + <item>com.android.systemui.privacy.television.TvOngoingPrivacyChip</item> <item>com.android.systemui.statusbar.tv.TvStatusBar</item> <item>com.android.systemui.statusbar.tv.notifications.TvNotificationPanel</item> <item>com.android.systemui.statusbar.tv.notifications.TvNotificationHandler</item> diff --git a/packages/SystemUI/res/values-television/dimens.xml b/packages/SystemUI/res/values-television/dimens.xml index 6da0c693f389..7626db93dd76 100644 --- a/packages/SystemUI/res/values-television/dimens.xml +++ b/packages/SystemUI/res/values-television/dimens.xml @@ -17,4 +17,8 @@ <resources> <!-- Opacity at which the background for the shutdown UI will be drawn. --> <item name="shutdown_scrim_behind_alpha" format="float" type="dimen">1.0</item> + + <dimen name="privacy_chip_icon_margin">3dp</dimen> + <dimen name="privacy_chip_icon_padding">8dp</dimen> + <dimen name="privacy_chip_icon_size">13dp</dimen> </resources>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index 6e458681bbab..be49e1f8c71e 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -170,7 +170,6 @@ <declare-styleable name="AlphaTintDrawableWrapper"> <attr name="android:tint" /> - <attr name="android:drawable" /> <attr name="android:alpha" /> </declare-styleable> @@ -191,9 +190,5 @@ <attr name="borderThickness" format="dimension" /> <attr name="borderColor" format="color" /> </declare-styleable> - - <declare-styleable name="RoundedCornerProgressDrawable"> - <attr name="android:drawable" /> - </declare-styleable> </resources> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 13c01102d032..f13fdd34e4f3 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -421,6 +421,9 @@ vibrator is capable of subtle vibrations --> <bool name="config_vibrateOnIconAnimation">false</bool> + <!-- Adjust the theme on fully custom and decorated custom view notifications --> + <bool name="config_adjustThemeOnNotificationCustomViews">false</bool> + <!-- If true, enable the advance anti-falsing classifier on the lockscreen. On some devices it does not work well, particularly with noisy touchscreens. Note that disabling it may increase the rate of unintentional unlocks. --> @@ -574,4 +577,7 @@ <!-- Whether to use the split 2-column notification shade --> <bool name="config_use_split_notification_shade">false</bool> + + <!-- Determines whether the shell features all run on another thread. --> + <bool name="config_enableShellMainThread">false</bool> </resources> diff --git a/packages/SystemUI/res/values/config_tv.xml b/packages/SystemUI/res/values/config_tv.xml deleted file mode 100644 index 2e776749582c..000000000000 --- a/packages/SystemUI/res/values/config_tv.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2016 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> - <!-- Whether to enable microphone disclosure indicator - (com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar). --> - <bool name="audio_recording_disclosure_enabled">true</bool> -</resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 4bc5a300e833..89c849a0ef4a 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -651,7 +651,7 @@ </dimen> <!-- The height of a notification header --> - <dimen name="notification_header_height">53dp</dimen> + <dimen name="notification_header_height">@*android:dimen/notification_header_height</dimen> <!-- The height of the gap between adjacent notification sections. --> <dimen name="notification_section_divider_height">@dimen/notification_side_paddings</dimen> diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml index d4bb128120e9..3e16cd47336c 100644 --- a/packages/SystemUI/res/values/flags.xml +++ b/packages/SystemUI/res/values/flags.xml @@ -42,4 +42,6 @@ <bool name="flag_lockscreen_animations">false</bool> <bool name="flag_toast_style">false</bool> + + <bool name="flag_navigation_bar_overlay">false</bool> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 70e8b8946ae6..4b95c16a3602 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2840,6 +2840,8 @@ <string name="empty_user_name" translatable="false">Your friend</string> <!-- Empty status shown before user has selected a friend [CHAR LIMIT=30] --> <string name="empty_status" translatable="false">Their status</string> + <!-- Default text for missed call notifications [CHAR LIMIT=30] --> + <string name="missed_call" translatable="false">Missed call</string> <!-- Title to display in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java index d2698ee9fe3b..b3a29a3fec78 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java @@ -190,7 +190,7 @@ public class TaskStackChangeListeners { } @Override - public void onActivityDismissingDockedStack() { + public void onActivityDismissingDockedTask() { mHandler.sendEmptyMessage(ON_ACTIVITY_DISMISSING_DOCKED_STACK); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index f511ed1c69c4..5f6fd30ffa1b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -29,17 +29,12 @@ import android.app.AlertDialog; 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; @@ -60,7 +55,6 @@ import com.android.internal.widget.LockPatternUtils; 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; @@ -105,12 +99,6 @@ public class KeyguardSecurityContainer extends FrameLayout { 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) { @@ -169,20 +157,16 @@ public class KeyguardSecurityContainer extends FrameLayout { // 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(); } @@ -240,136 +224,12 @@ public class KeyguardSecurityContainer extends FrameLayout { 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 = findKeyguardSecurityView(); - - if (securityView == null) { - return; - } - - FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) securityView.getLayoutParams(); - - if (mOneHandedMode) { - lp.gravity = Gravity.LEFT | Gravity.BOTTOM; - } else { - lp.gravity = Gravity.CENTER_HORIZONTAL; - } - - 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 = findKeyguardSecurityView(); - - 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); - } - } - - @Nullable - private KeyguardSecurityViewFlipper findKeyguardSecurityView() { - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - - if (isKeyguardSecurityView(child)) { - return (KeyguardSecurityViewFlipper) child; - } - } - - return null; - } - - private boolean isKeyguardSecurityView(View view) { - return view instanceof KeyguardSecurityViewFlipper; } public void onPause() { @@ -378,7 +238,6 @@ public class KeyguardSecurityContainer extends FrameLayout { mAlertDialog = null; } mSecurityViewFlipper.setWindowInsetsAnimationCallback(null); - mOrientationEventListener.disable(); } @Override @@ -460,44 +319,19 @@ public class KeyguardSecurityContainer extends FrameLayout { 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) { @@ -607,17 +441,18 @@ public class KeyguardSecurityContainer extends FrameLayout { 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); } @@ -655,44 +490,6 @@ public class KeyguardSecurityContainer extends FrameLayout { } } - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int maxHeight = 0; - int maxWidth = 0; - int childState = 0; - - int halfWidthMeasureSpec = MeasureSpec.makeMeasureSpec( - MeasureSpec.getSize(widthMeasureSpec) / 2, - MeasureSpec.getMode(widthMeasureSpec)); - - for (int i = 0; i < getChildCount(); i++) { - final View view = getChildAt(i); - if (view.getVisibility() != GONE) { - if (mOneHandedMode && isKeyguardSecurityView(view)) { - measureChildWithMargins(view, halfWidthMeasureSpec, 0, - heightMeasureSpec, 0); - } else { - measureChildWithMargins(view, widthMeasureSpec, 0, - heightMeasureSpec, 0); - } - final LayoutParams lp = (LayoutParams) view.getLayoutParams(); - maxWidth = Math.max(maxWidth, - view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); - maxHeight = Math.max(maxHeight, - view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); - childState = combineMeasuredStates(childState, view.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 fdab8db67431..1a8d420fb394 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -404,7 +404,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard 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 631c24844417..c77c86711abf 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java @@ -92,13 +92,4 @@ public class KeyguardSecurityModel { 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/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java index ec3188a6144f..5d226d562c29 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java @@ -29,6 +29,7 @@ import com.android.systemui.keyguard.dagger.KeyguardModule; import com.android.systemui.media.systemsounds.HomeSoundEffectController; import com.android.systemui.people.widget.PeopleSpaceWidgetEnabler; import com.android.systemui.power.PowerUI; +import com.android.systemui.privacy.television.TvOngoingPrivacyChip; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsModule; import com.android.systemui.shortcut.ShortcutKeyDispatcher; @@ -155,6 +156,12 @@ public abstract class SystemUIBinder { @ClassKey(TvNotificationPanel.class) public abstract SystemUI bindsTvNotificationPanel(TvNotificationPanel sysui); + /** Inject into TvOngoingPrivacyChip. */ + @Binds + @IntoMap + @ClassKey(TvOngoingPrivacyChip.class) + public abstract SystemUI bindsTvOngoingPrivacyChip(TvOngoingPrivacyChip sysui); + /** Inject into VolumeUI. */ @Binds @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java index 27ea64f85b11..70b7b047eebc 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java @@ -25,7 +25,6 @@ import android.hardware.display.DisplayManager; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import android.util.SparseArray; import android.view.Display; @@ -54,7 +53,6 @@ import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.recents.Recents; -import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.NotificationRemoteInputManager; @@ -116,7 +114,7 @@ public class NavigationBarController implements Callbacks, // Tracks config changes that will actually recreate the nav bar private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges( ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE - | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS + | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_UI_MODE); @Inject @@ -171,6 +169,7 @@ public class NavigationBarController implements Callbacks, configurationController.addCallback(this); mConfigChanges.applyNewConfig(mContext.getResources()); mNavBarOverlayController = navBarOverlayController; + mNavigationModeController.addListener(this); } @Override @@ -188,17 +187,18 @@ public class NavigationBarController implements Callbacks, @Override public void onNavigationModeChanged(int mode) { - // Workaround for b/132825155, for secondary users, we currently don't receive configuration - // changes on overlay package change since SystemUI runs for the system user. In this case, - // trigger a new configuration change to ensure that the nav bar is updated in the same way. - int userId = ActivityManagerWrapper.getInstance().getCurrentUserId(); - if (userId != UserHandle.USER_SYSTEM) { - mHandler.post(() -> { - for (int i = 0; i < mNavigationBars.size(); i++) { - recreateNavigationBar(mNavigationBars.keyAt(i)); + mHandler.post(() -> { + for (int i = 0; i < mNavigationBars.size(); i++) { + NavigationBar navBar = mNavigationBars.valueAt(i); + if (navBar == null) { + continue; } - }); - } + NavigationBarView view = (NavigationBarView) mNavigationBars.get(i).getView(); + if (view != null) { + view.updateStates(); + } + } + }); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java index c526c5d59552..62b9458447d8 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java @@ -21,6 +21,7 @@ import android.content.Context; import android.view.View; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.statusbar.FeatureFlags; import java.util.function.Consumer; @@ -31,16 +32,22 @@ import javax.inject.Inject; public class NavigationBarOverlayController { protected final Context mContext; + protected final FeatureFlags mFeatureFlags; @Inject - public NavigationBarOverlayController(Context context) { + public NavigationBarOverlayController(Context context, FeatureFlags featureFlags) { mContext = context; + mFeatureFlags = featureFlags; } public Context getContext() { return mContext; } + public boolean isNavigationBarOverlayEnabled() { + return mFeatureFlags.isNavigationBarOverlayEnabled(); + } + /** * Initialize the controller with visibility change callback and light/dark icon color. */ diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java index b55fa4d612f9..61e1d61e7909 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java @@ -192,6 +192,7 @@ public final class NavigationBarTransitions extends BarTransitions implements buttonDispatchers.valueAt(i).setDarkIntensity(darkIntensity); } mView.getRotationButtonController().setDarkIntensity(darkIntensity); + Dependency.get(NavigationBarOverlayController.class).setDarkIntensity(darkIntensity); for (DarkIntensityListener listener : mDarkIntensityListeners) { listener.onDarkIntensity(darkIntensity); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java index 8e75bec72c15..19e32783f765 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java @@ -171,6 +171,7 @@ public class NavigationBarView extends FrameLayout implements private NotificationPanelViewController mPanelView; private FloatingRotationButton mFloatingRotationButton; private RotationButtonController mRotationButtonController; + private NavigationBarOverlayController mNavBarOverlayController; /** * Helper that is responsible for showing the right toast when a disallowed activity operation @@ -339,8 +340,11 @@ public class NavigationBarView extends FrameLayout implements isGesturalMode ? mFloatingRotationButton : rotateSuggestionButton, mRotationButtonListener); - Dependency.get(NavigationBarOverlayController.class).init( - mNavbarOverlayVisibilityChangeCallback, mLightIconColor, mDarkIconColor); + mNavBarOverlayController = Dependency.get(NavigationBarOverlayController.class); + if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) { + mNavBarOverlayController.init( + mNavbarOverlayVisibilityChangeCallback, mLightIconColor, mDarkIconColor); + } mConfiguration = new Configuration(); mTmpLastConfiguration = new Configuration(); @@ -431,8 +435,9 @@ public class NavigationBarView extends FrameLayout implements // The visibility of the navigation bar buttons is dependent on the transient state of // the navigation bar. - Dependency.get(NavigationBarOverlayController.class).setButtonState( - isTransient, /* force */ false); + if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) { + mNavBarOverlayController.setButtonState(isTransient, /* force */ false); + } } void onBarTransition(int newMode) { @@ -666,7 +671,9 @@ public class NavigationBarView extends FrameLayout implements } mImeVisible = visible; mRotationButtonController.getRotationButton().setCanShowRotationButton(!mImeVisible); - Dependency.get(NavigationBarOverlayController.class).setCanShow(!mImeVisible); + if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) { + mNavBarOverlayController.setCanShow(!mImeVisible); + } } public void setDisabledFlags(int disabledFlags) { @@ -999,10 +1006,9 @@ public class NavigationBarView extends FrameLayout implements } else { updateButtonLocation(getRotateSuggestionButton(), inScreenSpace); } - final NavigationBarOverlayController navBarButtonsController = - Dependency.get(NavigationBarOverlayController.class); - if (navBarButtonsController.isVisible()) { - updateButtonLocation(navBarButtonsController.getCurrentView(), inScreenSpace); + if (mNavBarOverlayController.isNavigationBarOverlayEnabled() + && mNavBarOverlayController.isVisible()) { + updateButtonLocation(mNavBarOverlayController.getCurrentView(), inScreenSpace); } return mTmpRegion; } @@ -1230,7 +1236,9 @@ public class NavigationBarView extends FrameLayout implements if (mRotationButtonController != null) { mRotationButtonController.registerListeners(); } - Dependency.get(NavigationBarOverlayController.class).registerListeners(); + if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) { + mNavBarOverlayController.registerListeners(); + } getViewTreeObserver().addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener); updateNavButtonIcons(); @@ -1247,7 +1255,10 @@ public class NavigationBarView extends FrameLayout implements if (mRotationButtonController != null) { mRotationButtonController.unregisterListeners(); } - Dependency.get(NavigationBarOverlayController.class).unregisterListeners(); + + if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) { + mNavBarOverlayController.unregisterListeners(); + } mEdgeBackGestureHandler.onNavBarDetached(); getViewTreeObserver().removeOnComputeInternalInsetsListener( diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java index 65d4e23f7734..870e3bed2b7a 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java @@ -132,7 +132,7 @@ public class NavigationModeController implements Dumpable { Settings.Secure.putString(mCurrentUserContext.getContentResolver(), Secure.NAVIGATION_MODE, String.valueOf(mode))); if (DEBUG) { - Log.e(TAG, "updateCurrentInteractionMode: mode=" + mode); + Log.d(TAG, "updateCurrentInteractionMode: mode=" + mode); dumpAssetPaths(mCurrentUserContext); } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index 292cc7a7deaa..088743cd0f94 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -93,6 +93,9 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt( "gestures.back_timeout", 250); + private static final int MAX_NUM_LOGGED_PREDICTIONS = 10; + private static final int MAX_NUM_LOGGED_GESTURES = 10; + // Temporary log until b/176302696 is resolved static final boolean DEBUG_MISSING_GESTURE = true; static final String DEBUG_MISSING_GESTURE_TAG = "NoBackGesture"; @@ -222,8 +225,9 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa private String mPackageName; private float mMLResults; - private static final int MAX_LOGGED_PREDICTIONS = 10; + // For debugging private ArrayDeque<String> mPredictionLog = new ArrayDeque<>(); + private ArrayDeque<String> mGestureLog = new ArrayDeque<>(); private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver; @@ -607,7 +611,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa } // Check if we are within the tightest bounds beyond which // we would not need to run the ML model. - boolean withinRange = x <= mMLEnableWidth + mLeftInset + boolean withinRange = x < mMLEnableWidth + mLeftInset || x >= (mDisplaySize.x - mMLEnableWidth - mRightInset); if (!withinRange) { int results = -1; @@ -617,17 +621,20 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa // Denotes whether we should proceed with the gesture. // Even if it is false, we may want to log it assuming // it is not invalid due to exclusion. - withinRange = x <= mEdgeWidthLeft + mLeftInset + withinRange = x < mEdgeWidthLeft + mLeftInset || x >= (mDisplaySize.x - mEdgeWidthRight - mRightInset); } } // For debugging purposes - if (mPredictionLog.size() >= MAX_LOGGED_PREDICTIONS) { + if (mPredictionLog.size() >= MAX_NUM_LOGGED_PREDICTIONS) { mPredictionLog.removeFirst(); } - mPredictionLog.addLast(String.format("[%d,%d,%d,%f,%d]", - x, y, app, mMLResults, withinRange ? 1 : 0)); + mPredictionLog.addLast(String.format("Prediction [%d,%d,%d,%d,%f,%d]", + System.currentTimeMillis(), x, y, app, mMLResults, withinRange ? 1 : 0)); + if (DEBUG_MISSING_GESTURE) { + Log.d(DEBUG_MISSING_GESTURE_TAG, mPredictionLog.peekLast()); + } // Always allow if the user is in a transient sticky immersive state if (mIsNavBarShownTransiently) { @@ -689,6 +696,10 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa private void onMotionEvent(MotionEvent ev) { int action = ev.getActionMasked(); if (action == MotionEvent.ACTION_DOWN) { + if (DEBUG_MISSING_GESTURE) { + Log.d(DEBUG_MISSING_GESTURE_TAG, "Start gesture: " + ev); + } + // Verify if this is in within the touch region and we aren't in immersive mode, and // either the bouncer is showing or the notification panel is hidden mInputEventReceiver.setBatchingEnabled(false); @@ -709,6 +720,19 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa mEndPoint.set(-1, -1); mThresholdCrossed = false; } + + // For debugging purposes + if (mGestureLog.size() >= MAX_NUM_LOGGED_GESTURES) { + mGestureLog.removeFirst(); + } + mGestureLog.addLast(String.format( + "Gesture [%d,alw=%B,%B, %B,%B,disp=%s,wl=%d,il=%d,wr=%d,ir=%d,excl=%s]", + System.currentTimeMillis(), mAllowGesture, mIsOnLeftEdge, mIsBackGestureAllowed, + QuickStepContract.isBackGestureDisabled(mSysUiFlags), mDisplaySize, + mEdgeWidthLeft, mLeftInset, mEdgeWidthRight, mRightInset, mExcludeRegion)); + if (DEBUG_MISSING_GESTURE) { + Log.d(DEBUG_MISSING_GESTURE_TAG, mGestureLog.peekLast()); + } } else if (mAllowGesture || mLogGesture) { if (!mThresholdCrossed) { mEndPoint.x = (int) ev.getX(); @@ -827,18 +851,29 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa public void dump(PrintWriter pw) { pw.println("EdgeBackGestureHandler:"); pw.println(" mIsEnabled=" + mIsEnabled); + pw.println(" mIsAttached=" + mIsAttached); pw.println(" mIsBackGestureAllowed=" + mIsBackGestureAllowed); + pw.println(" mIsGesturalModeEnabled=" + mIsGesturalModeEnabled); + pw.println(" mIsNavBarShownTransiently=" + mIsNavBarShownTransiently); + pw.println(" mGestureBlockingActivityRunning=" + mGestureBlockingActivityRunning); pw.println(" mAllowGesture=" + mAllowGesture); + pw.println(" mUseMLModel=" + mUseMLModel); pw.println(" mDisabledForQuickstep=" + mDisabledForQuickstep); pw.println(" mStartingQuickstepRotation=" + mStartingQuickstepRotation); pw.println(" mInRejectedExclusion" + mInRejectedExclusion); pw.println(" mExcludeRegion=" + mExcludeRegion); pw.println(" mUnrestrictedExcludeRegion=" + mUnrestrictedExcludeRegion); - pw.println(" mIsAttached=" + mIsAttached); + pw.println(" mPipExcludedBounds=" + mPipExcludedBounds); pw.println(" mEdgeWidthLeft=" + mEdgeWidthLeft); pw.println(" mEdgeWidthRight=" + mEdgeWidthRight); - pw.println(" mIsNavBarShownTransiently=" + mIsNavBarShownTransiently); - pw.println(" mPredictionLog=" + String.join(";", mPredictionLog)); + pw.println(" mLeftInset=" + mLeftInset); + pw.println(" mRightInset=" + mRightInset); + pw.println(" mMLEnableWidth=" + mMLEnableWidth); + pw.println(" mMLModelThreshold=" + mMLModelThreshold); + pw.println(" mTouchSlop=" + mTouchSlop); + pw.println(" mBottomGestureHeight=" + mBottomGestureHeight); + pw.println(" mPredictionLog=" + String.join("\n", mPredictionLog)); + pw.println(" mGestureLog=" + String.join("\n", mGestureLog)); pw.println(" mEdgeBackPlugin=" + mEdgeBackPlugin); } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java index cd1131ba3e79..5dda23e4a47e 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java @@ -16,6 +16,7 @@ package com.android.systemui.people; +import static android.app.Notification.CATEGORY_MISSED_CALL; import static android.app.Notification.EXTRA_MESSAGES; import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY; import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY; @@ -189,7 +190,7 @@ public class PeopleSpaceUtils { tiles.addAll(recentTiles); } - tiles = augmentTilesFromVisibleNotifications(tiles, notificationEntryManager); + tiles = augmentTilesFromVisibleNotifications(context, tiles, notificationEntryManager); return tiles; } @@ -357,8 +358,8 @@ public class PeopleSpaceUtils { && storedUserId == userId; } - static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(List<PeopleSpaceTile> tiles, - NotificationEntryManager notificationEntryManager) { + static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(Context context, + List<PeopleSpaceTile> tiles, NotificationEntryManager notificationEntryManager) { if (notificationEntryManager == null) { Log.w(TAG, "NotificationEntryManager is null"); return tiles; @@ -374,12 +375,13 @@ public class PeopleSpaceUtils { } return tiles .stream() - .map(entry -> augmentTileFromVisibleNotifications(entry, visibleNotifications)) + .map(entry -> augmentTileFromVisibleNotifications( + context, entry, visibleNotifications)) .collect(Collectors.toList()); } - static PeopleSpaceTile augmentTileFromVisibleNotifications(PeopleSpaceTile tile, - Map<String, NotificationEntry> visibleNotifications) { + static PeopleSpaceTile augmentTileFromVisibleNotifications(Context context, + PeopleSpaceTile tile, Map<String, NotificationEntry> visibleNotifications) { String shortcutId = tile.getId(); String packageName = tile.getPackageName(); int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier(); @@ -389,7 +391,7 @@ public class PeopleSpaceUtils { return tile; } if (DEBUG) Log.d(TAG, "Augmenting tile from visible notifications, key:" + key); - return augmentTileFromNotification(tile, visibleNotifications.get(key).getSbn()); + return augmentTileFromNotification(context, tile, visibleNotifications.get(key).getSbn()); } /** @@ -408,7 +410,7 @@ public class PeopleSpaceUtils { } if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) { if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId); - storedTile = augmentTileFromNotification(storedTile, sbn); + storedTile = augmentTileFromNotification(context, storedTile, sbn); } else { if (DEBUG) { Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId); @@ -418,23 +420,40 @@ public class PeopleSpaceUtils { .setNotificationKey(null) .setNotificationContent(null) .setNotificationDataUri(null) + .setNotificationCategory(null) .build(); } updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, storedTile); } - static PeopleSpaceTile augmentTileFromNotification(PeopleSpaceTile tile, + static PeopleSpaceTile augmentTileFromNotification(Context context, PeopleSpaceTile tile, StatusBarNotification sbn) { - Notification.MessagingStyle.Message message = getLastMessagingStyleMessage(sbn); - if (message == null) { - if (DEBUG) Log.i(TAG, "Notification doesn't have content, skipping."); + Notification notification = sbn.getNotification(); + if (notification == null) { + if (DEBUG) Log.d(TAG, "Notification is null"); + return tile; + } + boolean isMissedCall = Objects.equals(notification.category, CATEGORY_MISSED_CALL); + Notification.MessagingStyle.Message message = getLastMessagingStyleMessage(notification); + + if (!isMissedCall && message == null) { + if (DEBUG) Log.d(TAG, "Notification has no content"); return tile; } + + // If it's a missed call notification and it doesn't include content, use fallback value, + // otherwise, use notification content. + boolean hasMessageText = message != null && !TextUtils.isEmpty(message.getText()); + CharSequence content = (isMissedCall && !hasMessageText) + ? context.getString(R.string.missed_call) : message.getText(); + Uri dataUri = message != null ? message.getDataUri() : null; + return tile .toBuilder() .setNotificationKey(sbn.getKey()) - .setNotificationContent(message.getText()) - .setNotificationDataUri(message.getDataUri()) + .setNotificationCategory(notification.category) + .setNotificationContent(content) + .setNotificationDataUri(dataUri) .build(); } @@ -462,6 +481,11 @@ public class PeopleSpaceUtils { * content, then birthdays, then the most recent status, and finally last interaction. */ private static RemoteViews getViewForTile(Context context, PeopleSpaceTile tile) { + if (Objects.equals(tile.getNotificationCategory(), CATEGORY_MISSED_CALL)) { + if (DEBUG) Log.d(TAG, "Create missed call view"); + return createMissedCallRemoteViews(context, tile); + } + if (tile.getNotificationKey() != null) { if (DEBUG) Log.d(TAG, "Create notification view"); return createNotificationRemoteViews(context, tile); @@ -630,6 +654,16 @@ public class PeopleSpaceUtils { return views; } + private static RemoteViews createMissedCallRemoteViews(Context context, + PeopleSpaceTile tile) { + RemoteViews views = new RemoteViews( + context.getPackageName(), R.layout.people_space_small_avatar_tile); + views.setTextViewText(R.id.status, tile.getNotificationContent()); + views.setImageViewResource(R.id.status_defined_icon, R.drawable.ic_phone_missed); + views.setBoolean(R.id.content_background, "setClipToOutline", true); + return views; + } + private static RemoteViews createNotificationRemoteViews(Context context, PeopleSpaceTile tile) { RemoteViews views = new RemoteViews( @@ -715,8 +749,7 @@ public class PeopleSpaceUtils { /** Gets the most recent {@link Notification.MessagingStyle.Message} from the notification. */ @VisibleForTesting public static Notification.MessagingStyle.Message getLastMessagingStyleMessage( - StatusBarNotification sbn) { - Notification notification = sbn.getNotification(); + Notification notification) { if (notification == null) { return null; } diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetEnabler.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetEnabler.java index b188acbf30f3..3df264421d75 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetEnabler.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetEnabler.java @@ -23,6 +23,7 @@ import android.util.Log; import com.android.systemui.SystemUI; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.people.PeopleSpaceActivity; import com.android.systemui.statusbar.FeatureFlags; import javax.inject.Inject; @@ -54,6 +55,12 @@ public class PeopleSpaceWidgetEnabler extends SystemUI { ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); + mContext.getPackageManager().setComponentEnabledSetting( + new ComponentName(mContext, PeopleSpaceActivity.class), + showPeopleSpace + ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED + : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + PackageManager.DONT_KILL_APP); } catch (Exception e) { Log.w(TAG, "Error enabling People Space widget:", e); } diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java index bee9889eaa4e..9e5c786b9a63 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -29,7 +29,6 @@ import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.util.Log; -import android.widget.RemoteViews; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.appwidget.IAppWidgetService; @@ -124,8 +123,6 @@ public class PeopleSpaceWidgetManager { */ public void updateWidgetWithNotificationChanged(StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction notificationAction) { - RemoteViews views = new RemoteViews( - mContext.getPackageName(), R.layout.people_space_small_avatar_tile); if (DEBUG) Log.d(TAG, "updateWidgetWithNotificationChanged called"); boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java index c9d1b71bca77..0fa7b59d0e54 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java +++ b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,9 +14,8 @@ * limitations under the License. */ -package com.android.systemui.statusbar.tv.micdisclosure; +package com.android.systemui.privacy.television; -import static android.provider.DeviceConfig.NAMESPACE_PRIVACY; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import android.animation.Animator; @@ -25,57 +24,56 @@ import android.animation.ObjectAnimator; import android.annotation.IntDef; import android.annotation.UiThread; import android.content.Context; +import android.content.res.Resources; +import android.graphics.Color; import android.graphics.PixelFormat; -import android.provider.DeviceConfig; -import android.text.TextUtils; -import android.util.ArraySet; +import android.graphics.drawable.Drawable; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.WindowManager; +import android.widget.ImageView; +import android.widget.LinearLayout; import com.android.systemui.R; -import com.android.systemui.statusbar.tv.TvStatusBar; +import com.android.systemui.SystemUI; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.privacy.PrivacyChipBuilder; +import com.android.systemui.privacy.PrivacyItem; +import com.android.systemui.privacy.PrivacyItemController; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Arrays; -import java.util.Collections; import java.util.List; -import java.util.Set; + +import javax.inject.Inject; /** - * A component of {@link TvStatusBar} responsible for notifying the user whenever an application is - * recording audio. - * - * @see TvStatusBar + * A SystemUI component responsible for notifying the user whenever an application is + * recording audio, accessing the camera or accessing the location. */ -public class AudioRecordingDisclosureBar implements - AudioActivityObserver.OnAudioActivityStateChangeListener { - private static final String TAG = "AudioRecordingDisclosure"; +@SysUISingleton +public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemController.Callback { + private static final String TAG = "TvOngoingPrivacyChip"; static final boolean DEBUG = false; - // This title is used to test the microphone disclosure indicator in - // CtsSystemUiHostTestCases:TvMicrophoneCaptureIndicatorTest + // This title is used in CameraMicIndicatorsPermissionTest and + // RecognitionServiceMicIndicatorTest. private static final String LAYOUT_PARAMS_TITLE = "MicrophoneCaptureIndicator"; - private static final String ENABLED_FLAG = "mic_disclosure_enabled"; - private static final String EXEMPT_PACKAGES_LIST = "mic_disclosure_exempt_packages"; - private static final String FORCED_PACKAGES_LIST = "mic_disclosure_forced_packages"; - @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"STATE_"}, value = { - STATE_STOPPED, STATE_NOT_SHOWN, STATE_APPEARING, STATE_SHOWN, STATE_DISAPPEARING }) - public @interface State {} + public @interface State { + } - private static final int STATE_STOPPED = -1; private static final int STATE_NOT_SHOWN = 0; private static final int STATE_APPEARING = 1; private static final int STATE_SHOWN = 2; @@ -84,106 +82,89 @@ public class AudioRecordingDisclosureBar implements private static final int ANIMATION_DURATION_MS = 200; private final Context mContext; - private boolean mIsEnabled; + private final PrivacyItemController mPrivacyItemController; private View mIndicatorView; private boolean mViewAndWindowAdded; private ObjectAnimator mAnimator; - @State private int mState = STATE_STOPPED; + private boolean mAllIndicatorsFlagEnabled; + private boolean mMicCameraIndicatorFlagEnabled; + private boolean mLocationIndicatorEnabled; + private List<PrivacyItem> mPrivacyItems; - /** - * Array of the observers that monitor different aspects of the system, such as AppOps and - * microphone foreground services - */ - private AudioActivityObserver[] mAudioActivityObservers; - /** - * Set of applications for which we make an exception and do not show the indicator. This gets - * populated once - in {@link #AudioRecordingDisclosureBar(Context)}. - */ - private final Set<String> mExemptPackages = new ArraySet<>(); + private LinearLayout mIconsContainer; + private final int mIconSize; + private final int mIconMarginStart; - public AudioRecordingDisclosureBar(Context context) { + @State + private int mState = STATE_NOT_SHOWN; + + @Inject + public TvOngoingPrivacyChip(Context context, PrivacyItemController privacyItemController) { + super(context); + Log.d(TAG, "Privacy chip running without id"); mContext = context; + mPrivacyItemController = privacyItemController; - // Load configs - reloadExemptPackages(); + Resources res = mContext.getResources(); + mIconMarginStart = Math.round(res.getDimension(R.dimen.privacy_chip_icon_margin)); + mIconSize = res.getDimensionPixelSize(R.dimen.privacy_chip_icon_size); - mIsEnabled = DeviceConfig.getBoolean(NAMESPACE_PRIVACY, ENABLED_FLAG, true); - // Start if enabled - if (mIsEnabled) { - start(); - } + mAllIndicatorsFlagEnabled = privacyItemController.getAllIndicatorsAvailable(); + mMicCameraIndicatorFlagEnabled = privacyItemController.getMicCameraAvailable(); + mLocationIndicatorEnabled = privacyItemController.getLocationAvailable(); - // Set up a config change listener - DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_PRIVACY, mContext.getMainExecutor(), - mConfigChangeListener); + if (DEBUG) { + Log.d(TAG, "allIndicators: " + mAllIndicatorsFlagEnabled); + Log.d(TAG, "micCameraIndicators: " + mMicCameraIndicatorFlagEnabled); + Log.d(TAG, "locationIndicators: " + mLocationIndicatorEnabled); + } } - private void reloadExemptPackages() { - mExemptPackages.clear(); - mExemptPackages.addAll(Arrays.asList(mContext.getResources().getStringArray( - R.array.audio_recording_disclosure_exempt_apps))); - mExemptPackages.addAll( - splitByComma( - DeviceConfig.getString(NAMESPACE_PRIVACY, EXEMPT_PACKAGES_LIST, null))); - mExemptPackages.removeAll( - splitByComma( - DeviceConfig.getString(NAMESPACE_PRIVACY, FORCED_PACKAGES_LIST, null))); + @Override + public void start() { + mPrivacyItemController.addCallback(this); } - @UiThread - private void start() { - if (mState != STATE_STOPPED) { - return; - } - mState = STATE_NOT_SHOWN; - - if (mAudioActivityObservers == null) { - mAudioActivityObservers = new AudioActivityObserver[]{ - new RecordAudioAppOpObserver(mContext, this), - new MicrophoneForegroundServicesObserver(mContext, this), - }; - } - - for (int i = mAudioActivityObservers.length - 1; i >= 0; i--) { - mAudioActivityObservers[i].start(); - } + @Override + public void onPrivacyItemsChanged(List<PrivacyItem> privacyItems) { + if (DEBUG) Log.d(TAG, "PrivacyItemsChanged"); + mPrivacyItems = privacyItems; + updateUI(); } - @UiThread - private void stop() { - if (mState == STATE_STOPPED) { - return; - } - mState = STATE_STOPPED; - - for (int i = mAudioActivityObservers.length - 1; i >= 0; i--) { - mAudioActivityObservers[i].stop(); - } - - // Remove the view if shown. - if (mState != STATE_NOT_SHOWN) { - removeIndicatorView(); - } + @Override + public void onFlagAllChanged(boolean flag) { + if (DEBUG) Log.d(TAG, "all indicators enabled: " + flag); + mAllIndicatorsFlagEnabled = flag; } - @UiThread @Override - public void onAudioActivityStateChange(boolean active, String packageName) { - if (DEBUG) { - Log.d(TAG, - "onAudioActivityStateChange, packageName=" + packageName + ", active=" - + active); - } + public void onFlagMicCameraChanged(boolean flag) { + if (DEBUG) Log.d(TAG, "mic/camera indicators enabled: " + flag); + mMicCameraIndicatorFlagEnabled = flag; + } - if (mExemptPackages.contains(packageName)) { - if (DEBUG) Log.d(TAG, " - exempt package: ignoring"); - return; - } + @Override + public void onFlagLocationChanged(boolean flag) { + if (DEBUG) Log.d(TAG, "location indicators enabled: " + flag); + mLocationIndicatorEnabled = flag; + } - if (active) { - showIfNeeded(); + private void updateUI() { + if (DEBUG) Log.d(TAG, mPrivacyItems.size() + " privacy items"); + + if ((mMicCameraIndicatorFlagEnabled || mAllIndicatorsFlagEnabled + || mLocationIndicatorEnabled) && !mPrivacyItems.isEmpty()) { + if (mState == STATE_NOT_SHOWN || mState == STATE_DISAPPEARING) { + showIndicator(); + } else { + if (DEBUG) Log.d(TAG, "only updating icons"); + PrivacyChipBuilder builder = new PrivacyChipBuilder(mContext, mPrivacyItems); + setIcons(builder.generateIcons(), mIconsContainer); + mIconsContainer.requestLayout(); + } } else { hideIndicatorIfNeeded(); } @@ -191,12 +172,7 @@ public class AudioRecordingDisclosureBar implements @UiThread private void hideIndicatorIfNeeded() { - // If STOPPED, NOT_SHOWN or DISAPPEARING - nothing else for us to do here. - if (mState != STATE_SHOWN && mState != STATE_APPEARING) return; - - if (hasActiveRecorders()) { - return; - } + if (mState == STATE_NOT_SHOWN || mState == STATE_DISAPPEARING) return; if (mViewAndWindowAdded) { mState = STATE_DISAPPEARING; @@ -210,23 +186,12 @@ public class AudioRecordingDisclosureBar implements } @UiThread - private void showIfNeeded() { - // If STOPPED, SHOWN or APPEARING - nothing else for us to do here. - if (mState != STATE_NOT_SHOWN && mState != STATE_DISAPPEARING) return; - - if (DEBUG) Log.d(TAG, "Showing indicator"); - - final int prevState = mState; + private void showIndicator() { mState = STATE_APPEARING; - if (prevState == STATE_DISAPPEARING) { - animateAppearance(); - return; - } - // Inflate the indicator view mIndicatorView = LayoutInflater.from(mContext).inflate( - R.layout.tv_audio_recording_indicator, null); + R.layout.tv_ongoing_privacy_chip, null); // 1. Set alpha to 0. // 2. Wait until the window is shown and the view is laid out. @@ -239,7 +204,7 @@ public class AudioRecordingDisclosureBar implements @Override public void onGlobalLayout() { // State could have changed to NOT_SHOWN (if all the recorders are - // already gone) to STOPPED (if the indicator was disabled) + // already gone) if (mState != STATE_APPEARING) return; mViewAndWindowAdded = true; @@ -251,22 +216,41 @@ public class AudioRecordingDisclosureBar implements } }); - final boolean isLtr = mContext.getResources().getConfiguration().getLayoutDirection() - == View.LAYOUT_DIRECTION_LTR; + mIconsContainer = mIndicatorView.findViewById(R.id.icons_container); + PrivacyChipBuilder builder = new PrivacyChipBuilder(mContext, mPrivacyItems); + setIcons(builder.generateIcons(), mIconsContainer); + final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams( WRAP_CONTENT, WRAP_CONTENT, WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT); - layoutParams.gravity = Gravity.TOP | (isLtr ? Gravity.RIGHT : Gravity.LEFT); + layoutParams.gravity = Gravity.TOP | Gravity.END; layoutParams.setTitle(LAYOUT_PARAMS_TITLE); layoutParams.packageName = mContext.getPackageName(); - final WindowManager windowManager = (WindowManager) mContext.getSystemService( - Context.WINDOW_SERVICE); + final WindowManager windowManager = mContext.getSystemService(WindowManager.class); windowManager.addView(mIndicatorView, layoutParams); + } + private void setIcons(List<Drawable> icons, ViewGroup iconsContainer) { + iconsContainer.removeAllViews(); + for (int i = 0; i < icons.size(); i++) { + Drawable icon = icons.get(i); + icon.mutate().setTint(Color.WHITE); + ImageView imageView = new ImageView(mContext); + imageView.setImageDrawable(icon); + imageView.setScaleType(ImageView.ScaleType.FIT_CENTER); + mIconsContainer.addView(imageView, mIconSize, mIconSize); + if (i != 0) { + ViewGroup.MarginLayoutParams layoutParams = + (ViewGroup.MarginLayoutParams) imageView.getLayoutParams(); + layoutParams.setMarginStart(mIconMarginStart); + imageView.setLayoutParams(layoutParams); + } + } + } private void animateAppearance() { animateAlphaTo(1f); @@ -333,22 +317,13 @@ public class AudioRecordingDisclosureBar implements } } - private boolean hasActiveRecorders() { - for (int index = mAudioActivityObservers.length - 1; index >= 0; index--) { - for (String activePackage : mAudioActivityObservers[index].getActivePackages()) { - if (mExemptPackages.contains(activePackage)) continue; - return true; - } - } - return false; - } - private void removeIndicatorView() { if (DEBUG) Log.d(TAG, "removeIndicatorView"); - final WindowManager windowManager = (WindowManager) mContext.getSystemService( - Context.WINDOW_SERVICE); - windowManager.removeView(mIndicatorView); + final WindowManager windowManager = mContext.getSystemService(WindowManager.class); + if (windowManager != null) { + windowManager.removeView(mIndicatorView); + } mIndicatorView = null; mAnimator = null; @@ -356,26 +331,4 @@ public class AudioRecordingDisclosureBar implements mViewAndWindowAdded = false; } - private static List<String> splitByComma(String string) { - return TextUtils.isEmpty(string) ? Collections.emptyList() : Arrays.asList( - string.split(",")); - } - - private final DeviceConfig.OnPropertiesChangedListener mConfigChangeListener = - new DeviceConfig.OnPropertiesChangedListener() { - @Override - public void onPropertiesChanged(DeviceConfig.Properties properties) { - reloadExemptPackages(); - - // Check if was enabled/disabled - if (mIsEnabled != properties.getBoolean(ENABLED_FLAG, true)) { - mIsEnabled = !mIsEnabled; - if (mIsEnabled) { - start(); - } else { - stop(); - } - } - } - }; } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java index 1386ddfa7692..53d9f1c08e6f 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java @@ -97,8 +97,8 @@ public class CropView extends View { float bottom = mBottomCrop + mBottomDelta; drawShade(canvas, 0, top); drawShade(canvas, bottom, 1f); - drawHandle(canvas, top); - drawHandle(canvas, bottom); + drawHandle(canvas, top, /* draw the handle tab down */ false); + drawHandle(canvas, bottom, /* draw the handle tab up */ true); } @Override @@ -122,7 +122,7 @@ public class CropView extends View { } else { // Bottom mBottomDelta = pixelsToFraction((int) MathUtils.constrain(delta, topPx + 2 * mCropTouchMargin - bottomPx, - getMeasuredHeight() - bottomPx)); + getHeight() - bottomPx)); } updateListener(event); invalidate(); @@ -212,21 +212,25 @@ public class CropView extends View { } private void drawShade(Canvas canvas, float fracStart, float fracEnd) { - canvas.drawRect(0, fractionToPixels(fracStart), getMeasuredWidth(), + canvas.drawRect(0, fractionToPixels(fracStart), getWidth(), fractionToPixels(fracEnd), mShadePaint); } - private void drawHandle(Canvas canvas, float frac) { + private void drawHandle(Canvas canvas, float frac, boolean handleTabUp) { int y = fractionToPixels(frac); - canvas.drawLine(0, y, getMeasuredWidth(), y, mHandlePaint); + canvas.drawLine(0, y, getWidth(), y, mHandlePaint); + float radius = 15 * getResources().getDisplayMetrics().density; + float x = getWidth() * .9f; + canvas.drawArc(x - radius, y - radius, x + radius, y + radius, handleTabUp ? 180 : 0, 180, + true, mHandlePaint); } private int fractionToPixels(float frac) { - return (int) (frac * getMeasuredHeight()); + return (int) (frac * getHeight()); } private float pixelsToFraction(int px) { - return px / (float) getMeasuredHeight(); + return px / (float) getHeight(); } private CropBoundary nearestBoundary(MotionEvent event, int topPx, int bottomPx) { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java index a6433ae94b2b..89efda98a5b6 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java @@ -46,6 +46,7 @@ public class LongScreenshotActivity extends Activity { private final UiEventLogger mUiEventLogger; private final ScrollCaptureController mScrollCaptureController; + private final ScrollCaptureClient.Connection mConnection; private ImageView mPreview; private View mSave; @@ -69,8 +70,10 @@ public class LongScreenshotActivity extends Activity { Context context) { mUiEventLogger = uiEventLogger; - mScrollCaptureController = new ScrollCaptureController(context, - ScreenshotController.sScrollConnection, mainExecutor, bgExecutor, imageExporter); + mScrollCaptureController = new ScrollCaptureController(context, mainExecutor, bgExecutor, + imageExporter); + + mConnection = ScreenshotController.takeScrollCaptureConnection(); } @Override @@ -98,15 +101,20 @@ public class LongScreenshotActivity extends Activity { public void onStart() { super.onStart(); if (mPreview.getDrawable() == null) { + if (mConnection == null) { + Log.e(TAG, "Failed to get scroll capture connection, bailing out"); + finishAndRemoveTask(); + return; + } doCapture(); } } - private void disableButtons() { - mSave.setEnabled(false); - mCancel.setEnabled(false); - mEdit.setEnabled(false); - mShare.setEnabled(false); + private void setButtonsEnabled(boolean enabled) { + mSave.setEnabled(enabled); + mCancel.setEnabled(enabled); + mEdit.setEnabled(enabled); + mShare.setEnabled(enabled); } private void doEdit(Uri uri) { @@ -115,8 +123,7 @@ public class LongScreenshotActivity extends Activity { if (!TextUtils.isEmpty(editorPackage)) { intent.setComponent(ComponentName.unflattenFromString(editorPackage)); } - intent.setType("image/png"); - intent.setData(uri); + intent.setDataAndType(uri, "image/png"); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); @@ -127,12 +134,11 @@ public class LongScreenshotActivity extends Activity { private void doShare(Uri uri) { Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("image/png"); - intent.setData(uri); + intent.putExtra(Intent.EXTRA_STREAM, uri); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION); Intent sharingChooserIntent = Intent.createChooser(intent, null) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_GRANT_READ_URI_PERMISSION); + .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivityAsUser(sharingChooserIntent, UserHandle.CURRENT); } @@ -140,7 +146,7 @@ public class LongScreenshotActivity extends Activity { private void onClicked(View v) { int id = v.getId(); v.setPressed(true); - disableButtons(); + setButtonsEnabled(false); if (id == R.id.save) { startExport(PendingAction.SAVE); } else if (id == R.id.cancel) { @@ -160,10 +166,12 @@ public class LongScreenshotActivity extends Activity { @Override public void onError() { Log.e(TAG, "Error exporting image data."); + setButtonsEnabled(true); } @Override public void onExportComplete(Uri outputUri) { + setButtonsEnabled(true); switch (action) { case EDIT: doEdit(outputUri); @@ -181,7 +189,8 @@ public class LongScreenshotActivity extends Activity { } private void doCapture() { - mScrollCaptureController.start(new ScrollCaptureController.ScrollCaptureCallback() { + mScrollCaptureController.start(mConnection, + new ScrollCaptureController.ScrollCaptureCallback() { @Override public void onError() { Log.e(TAG, "Error!"); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 31c693bdde1f..131fde6b4b56 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -101,7 +101,7 @@ import javax.inject.Inject; public class ScreenshotController { private static final String TAG = logTag(ScreenshotController.class); - public static ScrollCaptureClient.Connection sScrollConnection; + private static ScrollCaptureClient.Connection sScrollConnection; /** * POD used in the AsyncTask which saves an image in the background. @@ -222,6 +222,12 @@ public class ScreenshotController { | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS); + public static @Nullable ScrollCaptureClient.Connection takeScrollCaptureConnection() { + ScrollCaptureClient.Connection connection = sScrollConnection; + sScrollConnection = null; + return connection; + } + @Inject ScreenshotController( Context context, diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java index 863116a22ee4..4a3ffa45ab81 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java @@ -53,7 +53,6 @@ public class ScrollCaptureController { public static final int MAX_HEIGHT = 12000; - private final Connection mConnection; private final Context mContext; private final Executor mUiExecutor; @@ -65,10 +64,9 @@ public class ScrollCaptureController { private UUID mRequestId; private ScrollCaptureCallback mCaptureCallback; - public ScrollCaptureController(Context context, Connection connection, Executor uiExecutor, - Executor bgExecutor, ImageExporter exporter) { + public ScrollCaptureController(Context context, Executor uiExecutor, Executor bgExecutor, + ImageExporter exporter) { mContext = context; - mConnection = connection; mUiExecutor = uiExecutor; mBgExecutor = bgExecutor; mImageExporter = exporter; @@ -78,16 +76,17 @@ public class ScrollCaptureController { /** * Run scroll capture! * + * @param connection connection to the remote window to be used * @param callback request callback to report back to the service */ - public void start(ScrollCaptureCallback callback) { + public void start(Connection connection, ScrollCaptureCallback callback) { mCaptureTime = ZonedDateTime.now(); mRequestId = UUID.randomUUID(); mCaptureCallback = callback; float maxPages = Settings.Secure.getFloat(mContext.getContentResolver(), SETTING_KEY_MAX_PAGES, MAX_PAGES_DEFAULT); - mConnection.start(this::startCapture, maxPages); + connection.start(this::startCapture, maxPages); } /** diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java index a6aec3b7b1b7..0b40e225fb2b 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java @@ -17,8 +17,6 @@ package com.android.systemui.settings.brightness; import android.content.Context; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.LayerDrawable; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -29,10 +27,8 @@ import android.widget.SeekBar; import androidx.annotation.Nullable; import com.android.settingslib.RestrictedLockUtils; -import com.android.settingslib.Utils; import com.android.systemui.R; import com.android.systemui.statusbar.policy.BrightnessMirrorController; -import com.android.systemui.util.RoundedCornerProgressDrawable; import com.android.systemui.util.ViewController; import javax.inject.Inject; @@ -274,9 +270,6 @@ public class BrightnessSlider private BrightnessSlider fromTree(ViewGroup root, boolean useMirror) { BrightnessSliderView v = root.requireViewById(R.id.brightness_slider); - // TODO(175026098) Workaround. Remove when b/175026098 is fixed - applyTheme(v); - return new BrightnessSlider(root, v, useMirror); } @@ -286,32 +279,5 @@ public class BrightnessSlider ? R.layout.quick_settings_brightness_dialog_thick : R.layout.quick_settings_brightness_dialog; } - - private LayerDrawable findProgressClippableDrawable(BrightnessSliderView v) { - SeekBar b = v.requireViewById(R.id.slider); - if (b.getProgressDrawable() instanceof LayerDrawable) { - Drawable progress = ((LayerDrawable) b.getProgressDrawable()) - .findDrawableByLayerId(com.android.internal.R.id.progress); - if (progress instanceof RoundedCornerProgressDrawable) { - Drawable inner = ((RoundedCornerProgressDrawable) progress).getDrawable(); - if (inner instanceof LayerDrawable) { - return (LayerDrawable) inner; - } - } - } - return null; - } - - private void applyTheme(BrightnessSliderView v) { - LayerDrawable layer = findProgressClippableDrawable(v); - if (layer != null) { - layer.findDrawableByLayerId(R.id.slider_foreground).setTintList( - Utils.getColorAttr(v.getContext(), - com.android.internal.R.attr.colorControlActivated)); - layer.findDrawableByLayerId(R.id.slider_icon).setTintList( - Utils.getColorAttr(v.getContext(), - com.android.internal.R.attr.colorBackground)); - } - } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java index 862c27907e0f..cf77e290ebe3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java @@ -82,4 +82,8 @@ public class FeatureFlags { public boolean isMonetEnabled() { return mFlagReader.isEnabled(R.bool.flag_monet); } + + public boolean isNavigationBarOverlayEnabled() { + return mFlagReader.isEnabled(R.bool.flag_navigation_bar_overlay); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java b/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java index f2adaf042b2f..9ed9659c7ab8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java @@ -207,7 +207,7 @@ public class GestureRecorder { sb.append(g.toJson()); count++; } - mLastSaveLen = count; + mLastSaveLen += count; sb.append("]"); return sb.toString(); } @@ -249,7 +249,9 @@ public class GestureRecorder { public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { save(); if (mLastSaveLen >= 0) { - pw.println(String.valueOf(mLastSaveLen) + " gestures written to " + mLogfile); + pw.println(String.valueOf(mLastSaveLen) + + " gestures since last dump written to " + mLogfile); + mLastSaveLen = 0; } else { pw.println("error writing gestures"); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index 7c3b791aed09..c8c0755344a4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -548,8 +548,6 @@ public class NotificationEntryManager implements try { mStatusBarService.onNotificationClear( notification.getPackageName(), - notification.getTag(), - notification.getId(), notification.getUser().getIdentifier(), notification.getKey(), dismissedByUserStats.dismissalSurface, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java index d617dff372da..6b96ee4fc8e5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java @@ -254,8 +254,6 @@ public class NotifCollection implements Dumpable { try { mStatusBarService.onNotificationClear( entry.getSbn().getPackageName(), - entry.getSbn().getTag(), - entry.getSbn().getId(), entry.getSbn().getUser().getIdentifier(), entry.getSbn().getKey(), stats.dismissalSurface, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java index 5fff8c83048f..0ef4c4d3b788 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java @@ -36,12 +36,12 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; -import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; import com.android.internal.util.ContrastColorUtil; import com.android.internal.widget.CachingIconView; import com.android.settingslib.Utils; +import com.android.systemui.R; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.TransformableView; import com.android.systemui.statusbar.notification.TransformState; @@ -61,6 +61,7 @@ public abstract class NotificationViewWrapper implements TransformableView { private int mLightTextColor; private int mDarkTextColor; private int mDefaultTextColor; + private boolean mAdjustTheme; public static NotificationViewWrapper wrap(Context ctx, View v, ExpandableNotificationRow row) { if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) { @@ -97,6 +98,8 @@ public abstract class NotificationViewWrapper implements TransformableView { mView = view; mRow = row; onReinflated(); + mAdjustTheme = ctx.getResources().getBoolean( + R.bool.config_adjustThemeOnNotificationCustomViews); } /** @@ -124,12 +127,12 @@ public abstract class NotificationViewWrapper implements TransformableView { mLightTextColor = mView.getContext().getColor( com.android.internal.R.color.notification_primary_text_color_light); mDarkTextColor = mView.getContext().getColor( - R.color.notification_primary_text_color_dark); + com.android.internal.R.color.notification_primary_text_color_dark); Context themedContext = new ContextThemeWrapper(mView.getContext(), - R.style.Theme_DeviceDefault_DayNight); - mDefaultTextColor = Utils.getColorAttr(themedContext, R.attr.textColorPrimary) - .getDefaultColor(); + com.android.internal.R.style.Theme_DeviceDefault_DayNight); + mDefaultTextColor = Utils.getColorAttr(themedContext, + com.android.internal.R.attr.textColorPrimary).getDefaultColor(); } protected boolean needsInversion(int defaultBackgroundColor, View view) { @@ -208,7 +211,7 @@ public abstract class NotificationViewWrapper implements TransformableView { } protected void ensureThemeOnChildren() { - if (mView == null) { + if (!mAdjustTheme || mView == null) { return; } @@ -219,26 +222,20 @@ public abstract class NotificationViewWrapper implements TransformableView { } // Now let's check if there's unprotected text somewhere, and apply the theme if we find it. - if (!(mView instanceof ViewGroup)) { - return; - } - processChildrenTextColor((ViewGroup) mView); + processTextColorRecursive(mView); } - private void processChildrenTextColor(ViewGroup viewGroup) { - if (viewGroup == null) { - return; - } - - for (int i = 0; i < viewGroup.getChildCount(); i++) { - View child = viewGroup.getChildAt(i); - if (child instanceof TextView) { - int foreground = ((TextView) child).getCurrentTextColor(); - if (foreground == mLightTextColor || foreground == mDarkTextColor) { - ((TextView) child).setTextColor(mDefaultTextColor); - } - } else if (child instanceof ViewGroup) { - processChildrenTextColor((ViewGroup) child); + private void processTextColorRecursive(View view) { + if (view instanceof TextView) { + TextView textView = (TextView) view; + int foreground = textView.getCurrentTextColor(); + if (foreground == mLightTextColor || foreground == mDarkTextColor) { + textView.setTextColor(mDefaultTextColor); + } + } else if (view instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) view; + for (int i = 0; i < viewGroup.getChildCount(); i++) { + processTextColorRecursive(viewGroup.getChildAt(i)); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index 2ce403764c7d..d6380199e844 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -89,7 +89,8 @@ public class KeyguardClockPositionAlgorithm { private int mNotificationStackHeight; /** - * Minimum top margin to avoid overlap with status bar. + * Minimum top margin to avoid overlap with status bar, lock icon, or multi-user switcher + * avatar. */ private int mMinTopMargin; @@ -186,15 +187,15 @@ public class KeyguardClockPositionAlgorithm { /** * Sets up algorithm values. */ - public void setup(int statusBarMinHeight, int maxShadeBottom, int notificationStackHeight, - float panelExpansion, int parentHeight, int keyguardStatusHeight, - int userSwitchHeight, int clockPreferredY, int userSwitchPreferredY, - boolean hasCustomClock, boolean hasVisibleNotifs, float dark, float emptyDragAmount, - boolean bypassEnabled, int unlockedStackScrollerPadding, boolean showLockIcon, - float qsExpansion, int cutoutTopInset) { - mMinTopMargin = statusBarMinHeight + (showLockIcon - ? mContainerTopPaddingWithLockIcon : mContainerTopPaddingWithoutLockIcon) - + userSwitchHeight; + public void setup(int keyguardStatusBarHeaderHeight, int maxShadeBottom, + int notificationStackHeight, float panelExpansion, int parentHeight, + int keyguardStatusHeight, int userSwitchHeight, int clockPreferredY, + int userSwitchPreferredY, boolean hasCustomClock, boolean hasVisibleNotifs, float dark, + float emptyDragAmount, boolean bypassEnabled, int unlockedStackScrollerPadding, + boolean showLockIcon, float qsExpansion, int cutoutTopInset) { + mMinTopMargin = keyguardStatusBarHeaderHeight + Math.max(showLockIcon + ? mContainerTopPaddingWithLockIcon : mContainerTopPaddingWithoutLockIcon, + userSwitchHeight); mMaxShadeBottom = maxShadeBottom; mNotificationStackHeight = notificationStackHeight; mPanelExpansion = panelExpansion; 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 6940050f754b..093f57adb21a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -287,21 +287,6 @@ public class NotificationPanelViewController extends PanelViewController { } }; - final KeyguardUserSwitcherController.KeyguardUserSwitcherListener - mKeyguardUserSwitcherListener = - new KeyguardUserSwitcherController.KeyguardUserSwitcherListener() { - @Override - public void onKeyguardUserSwitcherChanged(boolean open) { - if (mKeyguardUserSwitcherController == null) { - updateUserSwitcherVisibility(false); - } else if (!mKeyguardUserSwitcherController.isSimpleUserSwitcher()) { - updateUserSwitcherVisibility(open - && mKeyguardStateController.isShowing() - && !mKeyguardStateController.isKeyguardFadingAway()); - } - } - }; - private final LayoutInflater mLayoutInflater; private final PowerManager mPowerManager; private final AccessibilityManager mAccessibilityManager; @@ -329,7 +314,6 @@ public class NotificationPanelViewController extends PanelViewController { private KeyguardAffordanceHelper mAffordanceHelper; private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController; - private boolean mKeyguardUserSwitcherIsShowing; private KeyguardUserSwitcherController mKeyguardUserSwitcherController; private KeyguardStatusBarView mKeyguardStatusBar; private ViewGroup mBigClockContainer; @@ -374,6 +358,7 @@ public class NotificationPanelViewController extends PanelViewController { private ValueAnimator mQsExpansionAnimator; private FlingAnimationUtils mFlingAnimationUtils; private int mStatusBarMinHeight; + private int mStatusBarHeaderHeightKeyguard; private int mNotificationsHeaderCollideDistance; private float mEmptyDragAmount; private float mDownX; @@ -772,6 +757,8 @@ public class NotificationPanelViewController extends PanelViewController { .setMaxLengthSeconds(0.4f).build(); mStatusBarMinHeight = mResources.getDimensionPixelSize( com.android.internal.R.dimen.status_bar_height); + mStatusBarHeaderHeightKeyguard = mResources.getDimensionPixelSize( + R.dimen.status_bar_header_height_keyguard); mQsPeekHeight = mResources.getDimensionPixelSize(R.dimen.qs_peek_height); mNotificationsHeaderCollideDistance = mResources.getDimensionPixelSize( R.dimen.header_notifications_collide_distance); @@ -808,7 +795,6 @@ public class NotificationPanelViewController extends PanelViewController { // Try to close the switcher so that callbacks are triggered if necessary. // Otherwise, NPV can get into a state where some of the views are still hidden mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(false); - mKeyguardUserSwitcherController.removeCallback(); } mKeyguardQsUserSwitchController = null; @@ -828,7 +814,6 @@ public class NotificationPanelViewController extends PanelViewController { mKeyguardUserSwitcherComponentFactory.build(keyguardUserSwitcherView); mKeyguardUserSwitcherController = userSwitcherComponent.getKeyguardUserSwitcherController(); - mKeyguardUserSwitcherController.setCallback(mKeyguardUserSwitcherListener); mKeyguardUserSwitcherController.init(); mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(true); } else { @@ -1069,7 +1054,7 @@ public class NotificationPanelViewController extends PanelViewController { int totalHeight = mView.getHeight(); int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding); int clockPreferredY = mKeyguardStatusViewController.getClockPreferredY(totalHeight); - int userSwitcherPreferredY = mStatusBarMinHeight; + int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard; boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled(); final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia(); @@ -1078,7 +1063,8 @@ public class NotificationPanelViewController extends PanelViewController { ? mKeyguardQsUserSwitchController.getUserIconHeight() : (mKeyguardUserSwitcherController != null ? mKeyguardUserSwitcherController.getUserIconHeight() : 0); - mClockPositionAlgorithm.setup(mStatusBarMinHeight, totalHeight - bottomPadding, + mClockPositionAlgorithm.setup(mStatusBarHeaderHeightKeyguard, + totalHeight - bottomPadding, mNotificationStackScrollLayoutController.getIntrinsicContentHeight(), getExpandedFraction(), totalHeight, @@ -3523,34 +3509,6 @@ public class NotificationPanelViewController extends PanelViewController { return false; } - private void updateUserSwitcherVisibility(boolean open) { - // Do not update if previously called with the same state. - if (mKeyguardUserSwitcherIsShowing == open) { - return; - } - mKeyguardUserSwitcherIsShowing = open; - - if (open) { - animateKeyguardStatusBarOut(); - mKeyguardStatusViewController.setKeyguardStatusViewVisibility( - mBarState, - true /* keyguardFadingAway */, - true /* goingToFullShade */, - mBarState); - setKeyguardBottomAreaVisibility(mBarState, true); - mNotificationContainerParent.setVisibility(View.GONE); - } else { - animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD); - mKeyguardStatusViewController.setKeyguardStatusViewVisibility( - StatusBarState.KEYGUARD, - false, - false, - StatusBarState.SHADE_LOCKED); - setKeyguardBottomAreaVisibility(mBarState, false); - mNotificationContainerParent.setVisibility(View.VISIBLE); - } - } - private void updateDisabledUdfpsController() { final boolean udfpsEnrolled = mAuthController.getUdfpsRegion() != null && mAuthController.isUdfpsEnrolled( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 041a97e1d404..b25fced6a212 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -78,6 +78,7 @@ import android.media.AudioAttributes; import android.metrics.LogMaker; import android.net.Uri; import android.os.AsyncTask; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -276,7 +277,8 @@ public class StatusBar extends SystemUI implements DemoMode, public static final boolean DEBUG = false; public static final boolean SPEW = false; public static final boolean DUMPTRUCK = true; // extra dumpsys info - public static final boolean DEBUG_GESTURES = false; + public static final boolean DEBUG_GESTURES = Build.IS_DEBUGGABLE; // TODO(b/178277858) + public static final boolean DEBUG_GESTURES_VERBOSE = true; public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false; public static final boolean DEBUG_CAMERA_LIFT = false; @@ -456,9 +458,7 @@ public class StatusBar extends SystemUI implements DemoMode, private final DisplayMetrics mDisplayMetrics; // XXX: gesture research - private final GestureRecorder mGestureRec = DEBUG_GESTURES - ? new GestureRecorder("/sdcard/statusbar_gestures.dat") - : null; + private GestureRecorder mGestureRec = null; private final ScreenPinningRequest mScreenPinningRequest; @@ -856,6 +856,10 @@ public class StatusBar extends SystemUI implements DemoMode, mActivityIntentHelper = new ActivityIntentHelper(mContext); DateTimeView.setReceiverHandler(timeTickHandler); + + if (DEBUG_GESTURES) { + mGestureRec = new GestureRecorder(mContext.getCacheDir() + "/statusbar_gestures.dat"); + } } @Override @@ -2267,7 +2271,7 @@ public class StatusBar extends SystemUI implements DemoMode, public boolean interceptTouchEvent(MotionEvent event) { if (DEBUG_GESTURES) { - if (event.getActionMasked() != MotionEvent.ACTION_MOVE) { + if (DEBUG_GESTURES_VERBOSE || event.getActionMasked() != MotionEvent.ACTION_MOVE) { EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH, event.getActionMasked(), (int) event.getX(), (int) event.getY(), mDisabled1, mDisabled2); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java index b76e451cb681..8845a05cf6f5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java @@ -19,9 +19,13 @@ package com.android.systemui.statusbar.policy; import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA; import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; import android.content.Context; import android.content.res.Resources; import android.database.DataSetObserver; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.os.UserHandle; @@ -50,7 +54,6 @@ import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.ViewController; -import java.lang.ref.WeakReference; import java.util.ArrayList; import javax.inject.Inject; @@ -73,9 +76,10 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS private final KeyguardUserAdapter mAdapter; private final KeyguardStateController mKeyguardStateController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private WeakReference<KeyguardUserSwitcherListener> mKeyguardUserSwitcherCallback; protected final SysuiStatusBarStateController mStatusBarStateController; private final KeyguardVisibilityHelper mKeyguardVisibilityHelper; + private ObjectAnimator mBgAnimator; + private final KeyguardUserSwitcherScrim mBackground; // Child views of KeyguardUserSwitcherView private KeyguardUserSwitcherListView mListView; @@ -171,6 +175,7 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS mUserSwitcherController, this); mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, keyguardStateController, dozeParameters); + mBackground = new KeyguardUserSwitcherScrim(context); } @Override @@ -204,6 +209,9 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS mKeyguardUpdateMonitor.registerCallback(mInfoCallback); mStatusBarStateController.addCallback(mStatusBarStateListener); mScreenLifecycle.addObserver(mScreenObserver); + mView.addOnLayoutChangeListener(mBackground); + mView.setBackground(mBackground); + mBackground.setAlpha(0); } @Override @@ -217,6 +225,9 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS mKeyguardUpdateMonitor.removeCallback(mInfoCallback); mStatusBarStateController.removeCallback(mStatusBarStateListener); mScreenLifecycle.removeObserver(mScreenObserver); + mView.removeOnLayoutChangeListener(mBackground); + mView.setBackground(null); + mBackground.setAlpha(0); } /** @@ -338,6 +349,13 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS animate); PropertyAnimator.setProperty(mListView, AnimatableProperty.TRANSLATION_X, -Math.abs(x), ANIMATION_PROPERTIES, animate); + + Rect r = new Rect(); + mListView.getDrawingRect(r); + mView.offsetDescendantRectToMyCoords(mListView, r); + mBackground.setGradientCenter( + (int) (mListView.getTranslationX() + r.left + r.width() / 2), + (int) (mListView.getTranslationY() + r.top + r.height() / 2)); } /** @@ -372,49 +390,52 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS } /** - * Remove the callback if it exists. - */ - public void removeCallback() { - if (DEBUG) Log.d(TAG, "removeCallback"); - mKeyguardUserSwitcherCallback = null; - } - - /** - * Register to receive notifications about keyguard user switcher state - * (see {@link KeyguardUserSwitcherListener}. - * - * Only one callback can be used at a time. - * - * @param callback The callback to register - */ - public void setCallback(KeyguardUserSwitcherListener callback) { - if (DEBUG) Log.d(TAG, "setCallback"); - mKeyguardUserSwitcherCallback = new WeakReference<>(callback); - } - - /** - * If user switcher state changes, notifies all {@link KeyguardUserSwitcherListener}. - * Switcher state is updatd before animations finish. + * NOTE: switcher state is updated before animations finish. * * @param animate true to animate transition. The user switcher state (i.e. * {@link #isUserSwitcherOpen()}) is updated before animation is finished. */ private void setUserSwitcherOpened(boolean open, boolean animate) { - boolean wasOpen = mUserSwitcherOpen; if (DEBUG) { - Log.d(TAG, String.format("setUserSwitcherOpened: %b -> %b (animate=%b)", wasOpen, - open, animate)); + Log.d(TAG, + String.format("setUserSwitcherOpened: %b -> %b (animate=%b)", + mUserSwitcherOpen, open, animate)); } mUserSwitcherOpen = open; - if (mUserSwitcherOpen != wasOpen) { - notifyUserSwitcherStateChanged(); - } updateVisibilities(animate); } private void updateVisibilities(boolean animate) { if (DEBUG) Log.d(TAG, String.format("updateVisibilities: animate=%b", animate)); mEndGuestButton.animate().cancel(); + if (mBgAnimator != null) { + mBgAnimator.cancel(); + } + + if (mUserSwitcherOpen) { + mBgAnimator = ObjectAnimator.ofInt(mBackground, "alpha", 0, 255); + mBgAnimator.setDuration(400); + mBgAnimator.setInterpolator(Interpolators.ALPHA_IN); + mBgAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mBgAnimator = null; + } + }); + mBgAnimator.start(); + } else { + mBgAnimator = ObjectAnimator.ofInt(mBackground, "alpha", 255, 0); + mBgAnimator.setDuration(400); + mBgAnimator.setInterpolator(Interpolators.ALPHA_OUT); + mBgAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mBgAnimator = null; + } + }); + mBgAnimator.start(); + } + if (mUserSwitcherOpen && mCurrentUserIsGuest) { // Show the "End guest session" button mEndGuestButton.setVisibility(View.VISIBLE); @@ -459,34 +480,6 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS return mUserSwitcherOpen; } - private void notifyUserSwitcherStateChanged() { - if (DEBUG) { - Log.d(TAG, String.format("notifyUserSwitcherStateChanged: mUserSwitcherOpen=%b", - mUserSwitcherOpen)); - } - if (mKeyguardUserSwitcherCallback != null) { - KeyguardUserSwitcherListener cb = mKeyguardUserSwitcherCallback.get(); - if (cb != null) { - cb.onKeyguardUserSwitcherChanged(mUserSwitcherOpen); - } - } - } - - /** - * Callback for keyguard user switcher state information - */ - public interface KeyguardUserSwitcherListener { - - /** - * Called when the keyguard enters or leaves user switcher mode. This will be called - * before the animations are finished. - * - * @param open if true, keyguard is showing the user switcher or transitioning from/to user - * switcher mode. - */ - void onKeyguardUserSwitcherChanged(boolean open); - } - static class KeyguardUserAdapter extends UserSwitcherController.BaseUserAdapter implements View.OnClickListener { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java index 49f5bcdd5a44..1d9d33d2aab1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java @@ -26,7 +26,6 @@ import android.graphics.RadialGradient; import android.graphics.Rect; import android.graphics.Shader; import android.graphics.drawable.Drawable; -import android.util.LayoutDirection; import android.view.View; import com.android.systemui.R; @@ -38,13 +37,14 @@ public class KeyguardUserSwitcherScrim extends Drawable implements View.OnLayoutChangeListener { private static final float OUTER_EXTENT = 2.5f; - private static final float INNER_EXTENT = 0.75f; + private static final float INNER_EXTENT = 0.25f; private int mDarkColor; - private int mTop; private int mAlpha = 255; private Paint mRadialGradientPaint = new Paint(); - private int mLayoutWidth; + private int mCircleX; + private int mCircleY; + private int mSize; public KeyguardUserSwitcherScrim(Context context) { mDarkColor = context.getColor( @@ -53,14 +53,11 @@ public class KeyguardUserSwitcherScrim extends Drawable @Override public void draw(Canvas canvas) { - boolean isLtr = getLayoutDirection() == LayoutDirection.LTR; + if (mAlpha == 0) { + return; + } Rect bounds = getBounds(); - float width = bounds.width() * OUTER_EXTENT; - float height = (mTop + bounds.height()) * OUTER_EXTENT; - canvas.translate(0, -mTop); - canvas.scale(1, height / width); - canvas.drawRect(isLtr ? bounds.right - width : 0, 0, - isLtr ? bounds.right : bounds.left + width, width, mRadialGradientPaint); + canvas.drawRect(bounds.left, bounds.top, bounds.right, bounds.bottom, mRadialGradientPaint); } @Override @@ -88,24 +85,36 @@ public class KeyguardUserSwitcherScrim extends Drawable public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom) { - mLayoutWidth = right - left; - mTop = top; + int width = right - left; + int height = bottom - top; + mSize = Math.max(width, height); updatePaint(); } } private void updatePaint() { - if (mLayoutWidth == 0) { + if (mSize == 0) { return; } - float radius = mLayoutWidth * OUTER_EXTENT; - boolean isLtr = getLayoutDirection() == LayoutDirection.LTR; + float outerRadius = mSize * OUTER_EXTENT; mRadialGradientPaint.setShader( - new RadialGradient(isLtr ? mLayoutWidth : 0, 0, radius, + new RadialGradient(mCircleX, mCircleY, outerRadius, new int[] { Color.argb( (int) (Color.alpha(mDarkColor) * mAlpha / 255f), 0, 0, 0), Color.TRANSPARENT }, - new float[] { Math.max(0f, mLayoutWidth * INNER_EXTENT / radius), 1f }, + new float[] { Math.max(0f, INNER_EXTENT / OUTER_EXTENT), 1f }, Shader.TileMode.CLAMP)); } + + /** + * Sets the center of the radial gradient used as a background + * + * @param x + * @param y + */ + public void setGradientCenter(int x, int y) { + mCircleX = x; + mCircleY = y; + updatePaint(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index bdf2b0c24ba5..37a763b740e1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -22,12 +22,10 @@ import android.os.RemoteException; import android.os.ServiceManager; import com.android.internal.statusbar.IStatusBarService; -import com.android.systemui.R; import com.android.systemui.SystemUI; import com.android.systemui.assist.AssistManager; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar; import javax.inject.Inject; @@ -36,11 +34,6 @@ import dagger.Lazy; /** * Status bar implementation for "large screen" products that mostly present no on-screen nav. * Serves as a collection of UI components, rather than showing its own UI. - * The following is the list of elements that constitute the TV-specific status bar: - * <ul> - * <li> {@link AudioRecordingDisclosureBar} - shown whenever applications are conducting audio - * recording, discloses the responsible applications </li> - * </ul> */ @SysUISingleton public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks { @@ -66,11 +59,6 @@ public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks { } catch (RemoteException ex) { // If the system process isn't there we're doomed anyway. } - - if (mContext.getResources().getBoolean(R.bool.audio_recording_disclosure_enabled)) { - // Creating AudioRecordingDisclosureBar and just letting it run - new AudioRecordingDisclosureBar(mContext); - } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioActivityObserver.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioActivityObserver.java deleted file mode 100644 index bbab6253a4d1..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioActivityObserver.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.tv.micdisclosure; - -import android.content.Context; - -import java.util.Set; - -/** - * A base class for implementing observers for different kinds of activities related to audio - * recording. These observers are to be initialized by {@link AudioRecordingDisclosureBar} and to - * report back to it. - */ -abstract class AudioActivityObserver { - - interface OnAudioActivityStateChangeListener { - void onAudioActivityStateChange(boolean active, String packageName); - } - - final Context mContext; - - final OnAudioActivityStateChangeListener mListener; - - AudioActivityObserver(Context context, OnAudioActivityStateChangeListener listener) { - mContext = context; - mListener = listener; - } - - abstract void start(); - - abstract void stop(); - - abstract Set<String> getActivePackages(); -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/MicrophoneForegroundServicesObserver.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/MicrophoneForegroundServicesObserver.java deleted file mode 100644 index 8caf95fb48f5..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/MicrophoneForegroundServicesObserver.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.tv.micdisclosure; - -import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE; - -import static com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar.DEBUG; - -import android.annotation.UiThread; -import android.app.ActivityManager; -import android.app.IActivityManager; -import android.app.IProcessObserver; -import android.content.Context; -import android.os.RemoteException; -import android.util.ArrayMap; -import android.util.Log; -import android.util.SparseArray; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * The purpose of these class is to detect packages that are running foreground services of type - * 'microphone' and to report back to {@link AudioRecordingDisclosureBar}. - */ -class MicrophoneForegroundServicesObserver extends AudioActivityObserver { - private static final String TAG = "MicrophoneForegroundServicesObserver"; - - private IActivityManager mActivityManager; - /** - * A dictionary that maps PIDs to the package names. We only keep track of the PIDs that are - * "active" (those that are running FGS with FOREGROUND_SERVICE_TYPE_MICROPHONE flag). - */ - private final SparseArray<String[]> mPidToPackages = new SparseArray<>(); - /** - * A dictionary that maps "active" packages to the number of the "active" processes associated - * with those packages. We really only need this in case when one application is running in - * multiple processes, so that we don't lose track of the package when one of its "active" - * processes ceases, while others remain "active". - */ - private final Map<String, Integer> mPackageToProcessCount = new ArrayMap<>(); - - MicrophoneForegroundServicesObserver(Context context, - OnAudioActivityStateChangeListener listener) { - super(context, listener); - } - - @Override - void start() { - mActivityManager = ActivityManager.getService(); - try { - mActivityManager.registerProcessObserver(mProcessObserver); - } catch (RemoteException e) { - Log.e(TAG, "Couldn't register process observer", e); - } - } - - @Override - void stop() { - try { - mActivityManager.unregisterProcessObserver(mProcessObserver); - } catch (RemoteException e) { - Log.e(TAG, "Couldn't unregister process observer", e); - } - mActivityManager = null; - mPackageToProcessCount.clear(); - } - - @Override - Set<String> getActivePackages() { - return mPackageToProcessCount.keySet(); - } - - @UiThread - private void onProcessForegroundServicesChanged(int pid, boolean hasMicFgs) { - final String[] changedPackages; - if (hasMicFgs) { - if (mPidToPackages.contains(pid)) { - // We are already tracking this pid - ignore. - changedPackages = null; - } else { - changedPackages = getPackageNames(pid); - mPidToPackages.append(pid, changedPackages); - } - } else { - changedPackages = mPidToPackages.removeReturnOld(pid); - } - - if (changedPackages == null) { - return; - } - - for (int index = changedPackages.length - 1; index >= 0; index--) { - final String packageName = changedPackages[index]; - int processCount = mPackageToProcessCount.getOrDefault(packageName, 0); - final boolean shouldNotify; - if (hasMicFgs) { - processCount++; - shouldNotify = processCount == 1; - } else { - processCount--; - shouldNotify = processCount == 0; - } - if (processCount > 0) { - mPackageToProcessCount.put(packageName, processCount); - } else { - mPackageToProcessCount.remove(packageName); - } - if (shouldNotify) notifyPackageStateChanged(packageName, hasMicFgs); - } - } - - @UiThread - private void onProcessDied(int pid) { - final String[] packages = mPidToPackages.removeReturnOld(pid); - if (packages == null) { - // This PID was not active - ignore. - return; - } - - for (int index = packages.length - 1; index >= 0; index--) { - final String packageName = packages[index]; - int processCount = mPackageToProcessCount.getOrDefault(packageName, 0); - if (processCount <= 0) { - Log.e(TAG, "Bookkeeping error, process count for " + packageName + " is " - + processCount); - continue; - } - processCount--; - if (processCount > 0) { - mPackageToProcessCount.put(packageName, processCount); - } else { - mPackageToProcessCount.remove(packageName); - notifyPackageStateChanged(packageName, false); - } - } - } - - @UiThread - private void notifyPackageStateChanged(String packageName, boolean active) { - if (DEBUG) { - Log.d(TAG, (active ? "New microphone fgs detected" : "Microphone fgs is gone") - + ", package=" + packageName); - } - - mListener.onAudioActivityStateChange(active, packageName); - } - - @UiThread - private String[] getPackageNames(int pid) { - final List<ActivityManager.RunningAppProcessInfo> runningApps; - try { - runningApps = mActivityManager.getRunningAppProcesses(); - } catch (RemoteException e) { - Log.d(TAG, "Couldn't get package name for pid=" + pid); - return null; - } - if (runningApps == null) { - Log.wtf(TAG, "No running apps reported"); - } - for (ActivityManager.RunningAppProcessInfo app : runningApps) { - if (app.pid == pid) { - return app.pkgList; - } - } - return null; - } - - private final IProcessObserver mProcessObserver = new IProcessObserver.Stub() { - @Override - public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {} - - @Override - public void onForegroundServicesChanged(int pid, int uid, int serviceTypes) { - mContext.getMainExecutor().execute(() -> onProcessForegroundServicesChanged(pid, - (serviceTypes & FOREGROUND_SERVICE_TYPE_MICROPHONE) != 0)); - } - - @Override - public void onProcessDied(int pid, int uid) { - mContext.getMainExecutor().execute( - () -> MicrophoneForegroundServicesObserver.this.onProcessDied(pid)); - } - }; -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/RecordAudioAppOpObserver.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/RecordAudioAppOpObserver.java deleted file mode 100644 index 9a2b4a93ac89..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/RecordAudioAppOpObserver.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.tv.micdisclosure; - -import static com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar.DEBUG; - -import android.annotation.UiThread; -import android.app.AppOpsManager; -import android.content.Context; -import android.util.ArraySet; -import android.util.Log; - -import java.util.Set; - -/** - * The purpose of these class is to detect packages that are conducting audio recording (according - * to {@link AppOpsManager}) and report this to {@link AudioRecordingDisclosureBar}. - */ -class RecordAudioAppOpObserver extends AudioActivityObserver implements - AppOpsManager.OnOpActiveChangedListener { - private static final String TAG = "RecordAudioAppOpObserver"; - - /** - * Set of the applications that currently are conducting audio recording according to {@link - * AppOpsManager}. - */ - private final Set<String> mActiveAudioRecordingPackages = new ArraySet<>(); - - RecordAudioAppOpObserver(Context context, OnAudioActivityStateChangeListener listener) { - super(context, listener); - } - - @Override - void start() { - if (DEBUG) { - Log.d(TAG, "Start"); - } - - // Register AppOpsManager callback - mContext.getSystemService(AppOpsManager.class) - .startWatchingActive( - new String[]{AppOpsManager.OPSTR_RECORD_AUDIO}, - mContext.getMainExecutor(), - this); - } - - @Override - void stop() { - if (DEBUG) { - Log.d(TAG, "Stop"); - } - - // Unregister AppOpsManager callback - mContext.getSystemService(AppOpsManager.class).stopWatchingActive(this); - - // Clean up state - mActiveAudioRecordingPackages.clear(); - } - - @UiThread - @Override - Set<String> getActivePackages() { - return mActiveAudioRecordingPackages; - } - - @UiThread - @Override - public void onOpActiveChanged(String op, int uid, String packageName, boolean active) { - if (DEBUG) { - Log.d(TAG, - "OP_RECORD_AUDIO active change, active=" + active + ", package=" - + packageName); - } - - if (active) { - if (mActiveAudioRecordingPackages.add(packageName)) { - mListener.onAudioActivityStateChange(true, packageName); - } - } else { - if (mActiveAudioRecordingPackages.remove(packageName)) { - mListener.onAudioActivityStateChange(false, packageName); - } - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java index 0a3e83326e01..9effc6728bab 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java @@ -94,6 +94,7 @@ public class ThemeOverlayApplier implements Dumpable { */ static final List<String> THEME_CATEGORIES = Lists.newArrayList( OVERLAY_CATEGORY_SYSTEM_PALETTE, + OVERLAY_CATEGORY_NEUTRAL_PALETTE, OVERLAY_CATEGORY_ICON_LAUNCHER, OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_FONT, @@ -107,6 +108,7 @@ public class ThemeOverlayApplier implements Dumpable { @VisibleForTesting static final Set<String> SYSTEM_USER_CATEGORIES = Sets.newHashSet( OVERLAY_CATEGORY_SYSTEM_PALETTE, + OVERLAY_CATEGORY_NEUTRAL_PALETTE, OVERLAY_CATEGORY_ACCENT_COLOR, OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE, @@ -129,8 +131,9 @@ public class ThemeOverlayApplier implements Dumpable { mLauncherPackage = launcherPackage; mThemePickerPackage = themePickerPackage; mTargetPackageToCategories.put(ANDROID_PACKAGE, Sets.newHashSet( - OVERLAY_CATEGORY_SYSTEM_PALETTE, OVERLAY_CATEGORY_ACCENT_COLOR, - OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_ICON_ANDROID)); + OVERLAY_CATEGORY_SYSTEM_PALETTE, OVERLAY_CATEGORY_NEUTRAL_PALETTE, + OVERLAY_CATEGORY_ACCENT_COLOR, OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE, + OVERLAY_CATEGORY_ICON_ANDROID)); mTargetPackageToCategories.put(SYSUI_PACKAGE, Sets.newHashSet(OVERLAY_CATEGORY_ICON_SYSUI)); mTargetPackageToCategories.put(SETTINGS_PACKAGE, diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index 1f222d80f014..028cbd084677 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -86,7 +86,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { protected static final int PRIMARY = 0; protected static final int SECONDARY = 1; - protected static final int NEUTRAL = 1; + protected static final int NEUTRAL = 2; // If lock screen wallpaper colors should also be considered when selecting the theme. // Doing this has performance impact, given that overlays would need to be swapped when diff --git a/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java b/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java index 79a197d9d409..a22793b05070 100644 --- a/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java @@ -16,15 +16,18 @@ package com.android.systemui.util; -import android.annotation.NonNull; -import android.annotation.Nullable; import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.Resources.Theme; import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; import android.graphics.drawable.DrawableWrapper; +import android.graphics.drawable.InsetDrawable; import android.util.AttributeSet; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.android.systemui.R; import org.xmlpull.v1.XmlPullParser; @@ -45,13 +48,18 @@ import java.io.IOException; * @attr ref R.styleable#AlphaTintDrawableWrapper_tint * @attr ref R.styleable#AlphaTintDrawableWrapper_alpha */ -public class AlphaTintDrawableWrapper extends DrawableWrapper { +public class AlphaTintDrawableWrapper extends InsetDrawable { private ColorStateList mTint; private int[] mThemeAttrs; /** No-arg constructor used by drawable inflation. */ public AlphaTintDrawableWrapper() { - super(null); + super(null, 0); + } + + AlphaTintDrawableWrapper(Drawable drawable, int[] themeAttrs) { + super(drawable, 0); + mThemeAttrs = themeAttrs; } @Override @@ -74,7 +82,7 @@ public class AlphaTintDrawableWrapper extends DrawableWrapper { public void applyTheme(Theme t) { super.applyTheme(t); - if (mThemeAttrs != null) { + if (mThemeAttrs != null && t != null) { final TypedArray a = t.resolveAttributes(mThemeAttrs, R.styleable.AlphaTintDrawableWrapper); updateStateFromTypedArray(a); @@ -92,9 +100,6 @@ public class AlphaTintDrawableWrapper extends DrawableWrapper { } private void updateStateFromTypedArray(@NonNull TypedArray a) { - if (a.hasValue(R.styleable.AlphaTintDrawableWrapper_android_drawable)) { - setDrawable(a.getDrawable(R.styleable.AlphaTintDrawableWrapper_android_drawable)); - } if (a.hasValue(R.styleable.AlphaTintDrawableWrapper_android_tint)) { mTint = a.getColorStateList(R.styleable.AlphaTintDrawableWrapper_android_tint); } @@ -109,4 +114,57 @@ public class AlphaTintDrawableWrapper extends DrawableWrapper { getDrawable().mutate().setTintList(mTint); } } + + @Nullable + @Override + public ConstantState getConstantState() { + return new AlphaTintState(super.getConstantState(), mThemeAttrs, getAlpha(), mTint); + } + + static class AlphaTintState extends Drawable.ConstantState { + + private ConstantState mWrappedState; + private int[] mThemeAttrs; + private int mAlpha; + private ColorStateList mColorStateList; + + AlphaTintState( + ConstantState wrappedState, + int[] themeAttrs, + int alpha, + ColorStateList colorStateList + ) { + mWrappedState = wrappedState; + mThemeAttrs = themeAttrs; + mAlpha = alpha; + mColorStateList = colorStateList; + } + + @NonNull + @Override + public Drawable newDrawable() { + return newDrawable(null, null); + } + + @NonNull + @Override + public Drawable newDrawable(Resources res, Theme theme) { + DrawableWrapper wrapper = (DrawableWrapper) mWrappedState.newDrawable(res, theme); + AlphaTintDrawableWrapper alphaTintDrawableWrapper = + new AlphaTintDrawableWrapper(wrapper.getDrawable(), mThemeAttrs); + alphaTintDrawableWrapper.setTintList(mColorStateList); + alphaTintDrawableWrapper.setAlpha(mAlpha); + return alphaTintDrawableWrapper; + } + + @Override + public boolean canApplyTheme() { + return true; + } + + @Override + public int getChangingConfigurations() { + return mWrappedState.getChangingConfigurations(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt index 1af2c9f46373..6aadd1020bce 100644 --- a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt +++ b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt @@ -17,15 +17,12 @@ package com.android.systemui.util import android.content.res.Resources -import android.content.res.TypedArray import android.graphics.Canvas import android.graphics.Path import android.graphics.Rect import android.graphics.drawable.Drawable import android.graphics.drawable.DrawableWrapper -import android.util.AttributeSet -import com.android.systemui.R -import org.xmlpull.v1.XmlPullParser +import android.graphics.drawable.InsetDrawable /** * [DrawableWrapper] to use in the progress of a slider. @@ -38,9 +35,9 @@ import org.xmlpull.v1.XmlPullParser * is meant to be smaller than the rounded corner. The background should have rounded corners that * are half of the height. */ -class RoundedCornerProgressDrawable(drawable: Drawable?) : DrawableWrapper(drawable) { - - constructor() : this(null) +class RoundedCornerProgressDrawable @JvmOverloads constructor( + drawable: Drawable? = null +) : InsetDrawable(drawable, 0) { companion object { private const val MAX_LEVEL = 10000 // Taken from Drawable @@ -52,35 +49,11 @@ class RoundedCornerProgressDrawable(drawable: Drawable?) : DrawableWrapper(drawa setClipPath(Rect()) } - override fun inflate( - r: Resources, - parser: XmlPullParser, - attrs: AttributeSet, - theme: Resources.Theme? - ) { - val a = obtainAttributes(r, theme, attrs, R.styleable.RoundedCornerProgressDrawable) - - // Inflation will advance the XmlPullParser and AttributeSet. - super.inflate(r, parser, attrs, theme) - - updateStateFromTypedArray(a) - if (drawable == null) { - throw IllegalStateException("${this::class.java.simpleName} needs a drawable") - } - a.recycle() - } - override fun onLayoutDirectionChanged(layoutDirection: Int): Boolean { onLevelChange(level) return super.onLayoutDirectionChanged(layoutDirection) } - private fun updateStateFromTypedArray(a: TypedArray) { - if (a.hasValue(R.styleable.RoundedCornerProgressDrawable_android_drawable)) { - setDrawable(a.getDrawable(R.styleable.RoundedCornerProgressDrawable_android_drawable)) - } - } - override fun onBoundsChange(bounds: Rect) { setClipPath(bounds) super.onBoundsChange(bounds) @@ -115,4 +88,24 @@ class RoundedCornerProgressDrawable(drawable: Drawable?) : DrawableWrapper(drawa super.draw(canvas) canvas.restore() } + + override fun getConstantState(): ConstantState? { + // This should not be null as it was created with a state in the constructor. + return RoundedCornerState(super.getConstantState()!!) + } + + private class RoundedCornerState(private val wrappedState: ConstantState) : ConstantState() { + override fun newDrawable(): Drawable { + return newDrawable(null, null) + } + + override fun newDrawable(res: Resources?, theme: Resources.Theme?): Drawable { + val wrapper = wrappedState.newDrawable(res, theme) as DrawableWrapper + return RoundedCornerProgressDrawable(wrapper.drawable) + } + + override fun getChangingConfigurations(): Int { + return wrappedState.changingConfigurations + } + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java index 06806d0e6ab6..6a648bdf8cd4 100644 --- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java @@ -347,6 +347,7 @@ public class ProximitySensor implements ThresholdSensor { public void check(long timeoutMs, Consumer<Boolean> callback) { if (!mSensor.isLoaded()) { callback.accept(null); + return; } mCallbacks.add(callback); if (!mRegistered.getAndSet(true)) { diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index fba0b0079012..4611fe03e157 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -31,6 +31,7 @@ import android.view.WindowManager; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.IStatusBarService; +import com.android.systemui.R; import com.android.systemui.dagger.WMComponent; import com.android.systemui.dagger.WMSingleton; import com.android.systemui.dagger.qualifiers.Main; @@ -97,7 +98,12 @@ import dagger.Provides; @Module public abstract class WMShellBaseModule { - private static final boolean ENABLE_SHELL_MAIN_THREAD = false; + /** + * Returns whether to enable a separate shell thread for the shell features. + */ + private static boolean enableShellMainThread(Context context) { + return context.getResources().getBoolean(R.bool.config_enableShellMainThread); + } // // Shell Concurrency - Components used for managing threading in the Shell and SysUI @@ -120,8 +126,8 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides @ShellMainThread - public static Handler provideShellMainHandler(@Main Handler sysuiMainHandler) { - if (ENABLE_SHELL_MAIN_THREAD) { + public static Handler provideShellMainHandler(Context context, @Main Handler sysuiMainHandler) { + if (enableShellMainThread(context)) { HandlerThread mainThread = new HandlerThread("wmshell.main"); mainThread.start(); return mainThread.getThreadHandler(); @@ -135,9 +141,9 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides @ShellMainThread - public static ShellExecutor provideShellMainExecutor(@ShellMainThread Handler mainHandler, - @Main ShellExecutor sysuiMainExecutor) { - if (ENABLE_SHELL_MAIN_THREAD) { + public static ShellExecutor provideShellMainExecutor(Context context, + @ShellMainThread Handler mainHandler, @Main ShellExecutor sysuiMainExecutor) { + if (enableShellMainThread(context)) { return new HandlerExecutor(mainHandler); } return sysuiMainExecutor; @@ -380,9 +386,9 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool, - @ShellMainThread ShellExecutor mainExecutor, + Context context, @ShellMainThread ShellExecutor mainExecutor, @ShellAnimationThread ShellExecutor animExecutor) { - return new Transitions(organizer, pool, mainExecutor, animExecutor); + return new Transitions(organizer, pool, context, mainExecutor, animExecutor); } // @@ -409,10 +415,11 @@ public abstract class WMShellBaseModule { ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, Context context, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, - @ShellMainThread ShellExecutor mainExecutor) { + @ShellMainThread ShellExecutor mainExecutor, + DisplayImeController displayImeController) { if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) { return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context, - rootTaskDisplayAreaOrganizer, mainExecutor)); + rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController)); } else { return Optional.empty(); } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index 997b488a627f..754b6a6435b4 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -104,9 +104,10 @@ public class WMShellModule { @Provides static AppPairsController provideAppPairs(ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, DisplayController displayController, - @ShellMainThread ShellExecutor mainExecutor) { + @ShellMainThread ShellExecutor mainExecutor, + DisplayImeController displayImeController) { return new AppPairsController(shellTaskOrganizer, syncQueue, displayController, - mainExecutor); + mainExecutor, displayImeController); } // diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java index 14b4d0262f60..854be1f76722 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java @@ -26,16 +26,11 @@ import static org.mockito.Mockito.when; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import android.testing.TestableResources; -import android.view.View; -import android.view.ViewGroup; import android.view.WindowInsetsController; -import android.widget.FrameLayout; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import org.junit.Before; @@ -50,21 +45,12 @@ import org.mockito.junit.MockitoRule; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper() public class KeyguardSecurityContainerTest extends SysuiTestCase { - private static final int SCREEN_WIDTH = 1600; - private static final int FAKE_MEASURE_SPEC = - View.MeasureSpec.makeMeasureSpec(SCREEN_WIDTH, View.MeasureSpec.EXACTLY); - - private static final SecurityMode ONE_HANDED_SECURITY_MODE = SecurityMode.PIN; - private static final SecurityMode TWO_HANDED_SECURITY_MODE = SecurityMode.Password; - - @Rule public MockitoRule mRule = MockitoJUnit.rule(); @Mock private WindowInsetsController mWindowInsetsController; - @Mock private KeyguardSecurityViewFlipper mSecurityViewFlipper; @@ -72,18 +58,9 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase { @Before public void setup() { - // Needed here, otherwise when mKeyguardSecurityContainer is created below, it'll cache - // the real references (rather than the TestableResources that this call creates). - mContext.ensureTestableResources(); - FrameLayout.LayoutParams securityViewFlipperLayoutParams = new FrameLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); - when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController); - when(mSecurityViewFlipper.getLayoutParams()).thenReturn(securityViewFlipperLayoutParams); mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext()); mKeyguardSecurityContainer.mSecurityViewFlipper = mSecurityViewFlipper; - mKeyguardSecurityContainer.addView(mSecurityViewFlipper, new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); } @Test @@ -92,75 +69,4 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase { verify(mWindowInsetsController).controlWindowInsetsAnimation(eq(ime()), anyLong(), any(), any(), any()); } - - @Test - public void onMeasure_usesFullWidthWithoutOneHandedMode() { - setUpKeyguard( - /* deviceConfigCanUseOneHandedKeyguard= */false, - /* sysuiResourceCanUseOneHandedKeyguard= */ false, - ONE_HANDED_SECURITY_MODE); - - mKeyguardSecurityContainer.onMeasure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC); - - verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC); - } - - @Test - public void onMeasure_usesFullWidthWithDeviceFlagDisabled() { - setUpKeyguard( - /* deviceConfigCanUseOneHandedKeyguard= */false, - /* sysuiResourceCanUseOneHandedKeyguard= */ true, - ONE_HANDED_SECURITY_MODE); - - mKeyguardSecurityContainer.onMeasure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC); - verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC); - } - - @Test - public void onMeasure_usesFullWidthWithSysUIFlagDisabled() { - setUpKeyguard( - /* deviceConfigCanUseOneHandedKeyguard= */true, - /* sysuiResourceCanUseOneHandedKeyguard= */ false, - ONE_HANDED_SECURITY_MODE); - - mKeyguardSecurityContainer.onMeasure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC); - verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC); - } - - @Test - public void onMeasure_usesHalfWidthWithFlagsEnabled() { - setUpKeyguard( - /* deviceConfigCanUseOneHandedKeyguard= */true, - /* sysuiResourceCanUseOneHandedKeyguard= */ true, - ONE_HANDED_SECURITY_MODE); - - int halfWidthMeasureSpec = - View.MeasureSpec.makeMeasureSpec(SCREEN_WIDTH / 2, View.MeasureSpec.EXACTLY); - mKeyguardSecurityContainer.onMeasure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC); - - verify(mSecurityViewFlipper).measure(halfWidthMeasureSpec, FAKE_MEASURE_SPEC); - } - - @Test - public void onMeasure_usesFullWidthForFullScreenIme() { - setUpKeyguard( - /* deviceConfigCanUseOneHandedKeyguard= */true, - /* sysuiResourceCanUseOneHandedKeyguard= */ true, - TWO_HANDED_SECURITY_MODE); - - mKeyguardSecurityContainer.onMeasure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC); - verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC); - } - - private void setUpKeyguard( - boolean deviceConfigCanUseOneHandedKeyguard, - boolean sysuiResourceCanUseOneHandedKeyguard, - SecurityMode securityMode) { - TestableResources testableResources = mContext.getOrCreateTestableResources(); - testableResources.addOverride(com.android.internal.R.bool.config_enableOneHandedKeyguard, - deviceConfigCanUseOneHandedKeyguard); - testableResources.addOverride(R.bool.can_use_one_handed_bouncer, - sysuiResourceCanUseOneHandedKeyguard); - mKeyguardSecurityContainer.updateLayoutForSecurityMode(securityMode); - } -} +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java index d79155cbb2fc..c8e939609e87 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java @@ -16,6 +16,7 @@ package com.android.systemui.people; +import static android.app.Notification.CATEGORY_MISSED_CALL; import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY; import static android.app.people.ConversationStatus.ACTIVITY_GAME; import static android.app.people.ConversationStatus.ACTIVITY_NEW_STORY; @@ -113,6 +114,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { private static final Uri URI = Uri.parse("fake_uri"); private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android); private static final String GAME_DESCRIPTION = "Playing a game!"; + private static final CharSequence MISSED_CALL = "Custom missed call message"; private static final String NAME = "username"; private static final Person PERSON = new Person.Builder() .setName("name") @@ -346,7 +348,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .build(); Notification.MessagingStyle.Message lastMessage = - PeopleSpaceUtils.getLastMessagingStyleMessage(sbn); + PeopleSpaceUtils.getLastMessagingStyleMessage(sbn.getNotification()); assertThat(lastMessage).isNull(); } @@ -447,7 +449,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .build(); Notification.MessagingStyle.Message lastMessage = - PeopleSpaceUtils.getLastMessagingStyleMessage(sbn); + PeopleSpaceUtils.getLastMessagingStyleMessage(sbn.getNotification()); assertThat(lastMessage.getText().toString()).isEqualTo(NOTIFICATION_TEXT_2); } @@ -465,7 +467,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .setUid(0) .build(); PeopleSpaceTile actual = PeopleSpaceUtils - .augmentTileFromNotification(tile, sbn); + .augmentTileFromNotification(mContext, tile, sbn); assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2); } @@ -483,9 +485,8 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .setUid(0) .build(); PeopleSpaceTile actual = PeopleSpaceUtils - .augmentTileFromNotification(tile, sbn); + .augmentTileFromNotification(mContext, tile, sbn); - assertThat(actual.getNotificationKey()).isEqualTo(null); assertThat(actual.getNotificationContent()).isEqualTo(null); } @@ -498,7 +499,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .setUid(0) .build(); PeopleSpaceTile actual = PeopleSpaceUtils - .augmentTileFromVisibleNotifications(tile, + .augmentTileFromVisibleNotifications(mContext, tile, Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1)); assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2); @@ -513,7 +514,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .setUid(0) .build(); PeopleSpaceTile actual = PeopleSpaceUtils - .augmentTileFromVisibleNotifications(tile, + .augmentTileFromVisibleNotifications(mContext, tile, Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1)); assertThat(actual.getNotificationContent()).isEqualTo(null); @@ -528,7 +529,8 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .setUid(0) .build(); List<PeopleSpaceTile> actualList = PeopleSpaceUtils - .augmentTilesFromVisibleNotifications(List.of(tile), mNotificationEntryManager); + .augmentTilesFromVisibleNotifications( + mContext, List.of(tile), mNotificationEntryManager); assertThat(actualList.size()).isEqualTo(1); assertThat(actualList.get(0).getNotificationContent().toString()) @@ -552,7 +554,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .setUid(0) .build(); List<PeopleSpaceTile> actualList = PeopleSpaceUtils - .augmentTilesFromVisibleNotifications(List.of(tile1, tile2), + .augmentTilesFromVisibleNotifications(mContext, List.of(tile1, tile2), mNotificationEntryManager); assertThat(actualList.size()).isEqualTo(2); @@ -763,6 +765,33 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { } @Test + public void testCreateRemoteViewsWithMissedCallNotification() { + PeopleSpaceTile tileWithMissedCallNotification = PERSON_TILE.toBuilder() + .setNotificationDataUri(null) + .setNotificationCategory(CATEGORY_MISSED_CALL) + .setNotificationContent(MISSED_CALL) + .build(); + RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext, + tileWithMissedCallNotification, 0); + View result = views.apply(mContext, null); + + TextView name = (TextView) result.findViewById(R.id.name); + assertEquals(name.getText(), NAME); + // Has availability. + View availability = result.findViewById(R.id.availability); + assertEquals(View.GONE, availability.getVisibility()); + // Has new story. + View personIcon = result.findViewById(R.id.person_icon_only); + View personIconWithStory = result.findViewById(R.id.person_icon_with_story); + assertEquals(View.VISIBLE, personIcon.getVisibility()); + assertEquals(View.GONE, personIconWithStory.getVisibility()); + // Has status. + TextView statusContent = (TextView) result.findViewById(R.id.status); + assertEquals(statusContent.getText(), MISSED_CALL); + } + + + @Test public void testCreateRemoteViewsWithNotificationTemplate() { PeopleSpaceTile tileWithStatusAndNotification = PERSON_TILE.toBuilder() .setNotificationDataUri(null) diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java index 9470141178dd..1c8324c524f4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java @@ -16,6 +16,7 @@ package com.android.systemui.people.widget; +import static android.app.Notification.CATEGORY_MISSED_CALL; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_HIGH; @@ -167,7 +168,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { int[] widgetIdsArray = {}; when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); - StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID); + StatusBarNotification sbn = createNotification( + OTHER_SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ false); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setSbn(sbn) .setId(1)); @@ -207,7 +209,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbnWithoutPackageName = new SbnBuilder() - .setNotification(createMessagingStyleNotification(SHORTCUT_ID)) + .setNotification(createMessagingStyleNotification( + SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ false)) .build(); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setSbn(sbnWithoutPackageName) @@ -256,7 +259,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); - StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID); + StatusBarNotification sbn = createNotification( + OTHER_SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setSbn(sbn) .setId(1)); @@ -276,7 +280,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder() - .setNotification(createMessagingStyleNotification(SHORTCUT_ID)) + .setNotification(createMessagingStyleNotification( + SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ false)) .setPkg(TEST_PACKAGE_B) .build(); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() @@ -295,7 +300,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); - StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID); + StatusBarNotification sbn = createNotification( + OTHER_SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setSbn(sbn) .setId(1)); @@ -315,7 +321,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder() - .setNotification(createMessagingStyleNotification(SHORTCUT_ID)) + .setNotification(createMessagingStyleNotification( + SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false)) .setPkg(TEST_PACKAGE_B) .build(); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() @@ -337,7 +344,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() - .setSbn(createConversationNotification(SHORTCUT_ID)) + .setSbn(createNotification( + SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false)) .setId(1)); mClock.advanceTime(MIN_LINGER_DURATION); @@ -367,7 +375,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() - .setSbn(createConversationNotification(SHORTCUT_ID)) + .setSbn(createNotification( + SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false)) .setId(1)); mClock.advanceTime(MIN_LINGER_DURATION); @@ -400,7 +409,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { PeopleSpaceUtils.removeStorageForTile(mContext, SECOND_WIDGET_ID_WITH_SHORTCUT); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() - .setSbn(createConversationNotification(SHORTCUT_ID)) + .setSbn(createNotification( + SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false)) .setId(1)); mClock.advanceTime(MIN_LINGER_DURATION); @@ -417,33 +427,52 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test - public void testDoNotUpdateNotificationPostedWithoutMessagesIfExistingTile() + public void testUpdateMissedCallNotificationWithoutContentPostedIfExistingTile() throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT); - Notification notificationWithoutMessagingStyle = new Notification.Builder(mContext) - .setContentTitle("TEST_TITLE") - .setContentText("TEST_TEXT") - .setShortcutId(SHORTCUT_ID) - .build(); - StatusBarNotification sbn = new SbnBuilder() - .setNotification(notificationWithoutMessagingStyle) - .setPkg(TEST_PACKAGE_A) - .setUid(0) - .build(); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() - .setSbn(sbn) + .setSbn(createNotification( + SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ true)) .setId(1)); mClock.advanceTime(MIN_LINGER_DURATION); verify(mAppWidgetManager, times(1)) .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), mBundleArgumentCaptor.capture()); - Bundle options = requireNonNull(mBundleArgumentCaptor.getValue()); - assertThat((PeopleSpaceTile) options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE)) - .isEqualTo(PERSON_TILE); + Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue()); + + PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE); + assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY); + assertThat(tile.getNotificationContent()) + .isEqualTo(mContext.getString(R.string.missed_call)); + verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), + any()); + } + + @Test + public void testUpdateMissedCallNotificationWithContentPostedIfExistingTile() + throws Exception { + int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; + when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT); + + NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() + .setSbn(createNotification( + SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ true)) + .setId(1)); + mClock.advanceTime(MIN_LINGER_DURATION); + + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + mBundleArgumentCaptor.capture()); + Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue()); + + PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE); + assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY); + assertThat(tile.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT); verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), any()); } @@ -453,7 +482,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); - StatusBarNotification sbn = createConversationNotification(SHORTCUT_ID); + StatusBarNotification sbn = createNotification( + SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setSbn(sbn) .setId(1)); @@ -483,21 +513,29 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { return convo; } - private Notification createMessagingStyleNotification(String shortcutId) { - return new Notification.Builder(mContext) + private Notification createMessagingStyleNotification(String shortcutId, + boolean isMessagingStyle, boolean isMissedCall) { + Notification.Builder builder = new Notification.Builder(mContext) .setContentTitle("TEST_TITLE") .setContentText("TEST_TEXT") - .setShortcutId(shortcutId) - .setStyle(new Notification.MessagingStyle(PERSON) - .addMessage( - new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT, 10, - PERSON)) - ) - .build(); + .setShortcutId(shortcutId); + if (isMessagingStyle) { + builder.setStyle(new Notification.MessagingStyle(PERSON) + .addMessage( + new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT, 10, + PERSON)) + ); + } + if (isMissedCall) { + builder.setCategory(CATEGORY_MISSED_CALL); + } + return builder.build(); } - private StatusBarNotification createConversationNotification(String shortcutId) { - Notification notification = createMessagingStyleNotification(shortcutId); + private StatusBarNotification createNotification(String shortcutId, + boolean isMessagingStyle, boolean isMissedCall) { + Notification notification = createMessagingStyleNotification( + shortcutId, isMessagingStyle, isMissedCall); return new SbnBuilder() .setNotification(notification) .setPkg(TEST_PACKAGE_A) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java index ce0f1220fc88..9a5482c33501 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java @@ -377,8 +377,6 @@ public class NotifCollectionTest extends SysuiTestCase { // THEN we send the dismissal to system server verify(mStatusBarService).onNotificationClear( notif2.sbn.getPackageName(), - notif2.sbn.getTag(), - 88, notif2.sbn.getUser().getIdentifier(), notif2.sbn.getKey(), stats.dismissalSurface, @@ -528,8 +526,6 @@ public class NotifCollectionTest extends SysuiTestCase { // THEN we never send the dismissal to system server verify(mStatusBarService, never()).onNotificationClear( notif.sbn.getPackageName(), - notif.sbn.getTag(), - 47, notif.sbn.getUser().getIdentifier(), notif.sbn.getKey(), stats.dismissalSurface, @@ -566,8 +562,6 @@ public class NotifCollectionTest extends SysuiTestCase { // THEN the notification is never sent to system server to dismiss verify(mStatusBarService, never()).onNotificationClear( eq(notif.sbn.getPackageName()), - eq(notif.sbn.getTag()), - eq(47), eq(notif.sbn.getUser().getIdentifier()), eq(notif.sbn.getKey()), anyInt(), @@ -596,8 +590,6 @@ public class NotifCollectionTest extends SysuiTestCase { // THEN we send the dismissal to system server verify(mStatusBarService).onNotificationClear( eq(notif.sbn.getPackageName()), - eq(notif.sbn.getTag()), - eq(47), eq(notif.sbn.getUser().getIdentifier()), eq(notif.sbn.getKey()), anyInt(), @@ -1125,8 +1117,6 @@ public class NotifCollectionTest extends SysuiTestCase { // THEN we send the dismissals to system server verify(mStatusBarService).onNotificationClear( notif1.sbn.getPackageName(), - notif1.sbn.getTag(), - 47, notif1.sbn.getUser().getIdentifier(), notif1.sbn.getKey(), stats1.dismissalSurface, @@ -1135,8 +1125,6 @@ public class NotifCollectionTest extends SysuiTestCase { verify(mStatusBarService).onNotificationClear( notif2.sbn.getPackageName(), - notif2.sbn.getTag(), - 88, notif2.sbn.getUser().getIdentifier(), notif2.sbn.getKey(), stats2.dismissalSurface, diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java index 45828c3f73ad..e798207c6947 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java @@ -23,6 +23,7 @@ import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_IC import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_SETTINGS; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_SYSUI; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_THEME_PICKER; +import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_NEUTRAL_PALETTE; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SHAPE; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE; import static com.android.systemui.theme.ThemeOverlayApplier.SETTINGS_PACKAGE; @@ -114,6 +115,8 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { ANDROID_PACKAGE, OVERLAY_CATEGORY_ACCENT_COLOR, false), createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_SYSTEM_PALETTE, ANDROID_PACKAGE, OVERLAY_CATEGORY_SYSTEM_PALETTE, false), + createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_NEUTRAL_PALETTE, + ANDROID_PACKAGE, OVERLAY_CATEGORY_NEUTRAL_PALETTE, false), createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_FONT, ANDROID_PACKAGE, OVERLAY_CATEGORY_FONT, false), createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_SHAPE, @@ -124,6 +127,8 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { ANDROID_PACKAGE, OVERLAY_CATEGORY_ACCENT_COLOR, true), createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_SYSTEM_PALETTE, ANDROID_PACKAGE, OVERLAY_CATEGORY_SYSTEM_PALETTE, true), + createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_NEUTRAL_PALETTE, + ANDROID_PACKAGE, OVERLAY_CATEGORY_NEUTRAL_PALETTE, true), createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_FONT, ANDROID_PACKAGE, OVERLAY_CATEGORY_FONT, true), createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_SHAPE, diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java index c5a197eef2d4..242fe9f5fffe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java @@ -16,6 +16,8 @@ package com.android.systemui.util.sensors; +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.assertNull; @@ -86,6 +88,29 @@ public class ProximityCheckTest extends SysuiTestCase { } @Test + public void testNotLoaded() { + mFakeProximitySensor.setSensorAvailable(false); + + assertThat(mTestableCallback.mLastResult).isNull(); + assertThat(mTestableCallback.mNumCalls).isEqualTo(0); + + mProximityCheck.check(100, mTestableCallback); + + assertThat(mTestableCallback.mLastResult).isNull(); + assertThat(mTestableCallback.mNumCalls).isEqualTo(1); + + mFakeProximitySensor.setSensorAvailable(true); + + mProximityCheck.check(100, mTestableCallback); + + mFakeProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(true, 0)); + mFakeProximitySensor.alertListeners(); + + assertThat(mTestableCallback.mLastResult).isNotNull(); + assertThat(mTestableCallback.mNumCalls).isEqualTo(2); + } + + @Test public void testProxDoesntCancelOthers() { assertFalse(mFakeProximitySensor.isRegistered()); // We don't need our "other" listener to do anything. Just ensure our sensor is registered. diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java index 6dcad255eee4..3502baa99b6d 100644 --- a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java +++ b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java @@ -23,7 +23,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; -import android.net.ConnectivityManager; +import android.net.VpnManager; import android.os.Bundle; import android.os.UserHandle; import android.provider.Settings; @@ -42,7 +42,7 @@ public class AlwaysOnDisconnectedDialog extends AlertActivity private static final String TAG = "VpnDisconnected"; - private ConnectivityManager mService; + private VpnManager mService; private int mUserId; private String mVpnPackage; @@ -51,8 +51,8 @@ public class AlwaysOnDisconnectedDialog extends AlertActivity super.onCreate(savedInstanceState); mUserId = UserHandle.myUserId(); - final ConnectivityManager cm = getSystemService(ConnectivityManager.class); - mVpnPackage = cm.getAlwaysOnVpnPackageForUser(mUserId); + final VpnManager vm = getSystemService(VpnManager.class); + mVpnPackage = vm.getAlwaysOnVpnPackageForUser(mUserId); if (mVpnPackage == null) { finish(); return; diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java index aab01d03b96d..fb2367843fc1 100644 --- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java +++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java @@ -21,7 +21,6 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTE import android.content.DialogInterface; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; -import android.net.ConnectivityManager; import android.net.VpnManager; import android.os.Bundle; import android.os.UserHandle; @@ -45,7 +44,6 @@ public class ConfirmDialog extends AlertActivity private String mPackage; - private ConnectivityManager mCm; // TODO: switch entirely to VpnManager once VPN code moves private VpnManager mVm; public ConfirmDialog() { @@ -60,7 +58,6 @@ public class ConfirmDialog extends AlertActivity protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPackage = getCallingPackage(); - mCm = getSystemService(ConnectivityManager.class); mVm = getSystemService(VpnManager.class); if (mVm.prepareVpn(mPackage, null, UserHandle.myUserId())) { @@ -72,7 +69,7 @@ public class ConfirmDialog extends AlertActivity finish(); return; } - final String alwaysOnVpnPackage = mCm.getAlwaysOnVpnPackageForUser(UserHandle.myUserId()); + final String alwaysOnVpnPackage = mVm.getAlwaysOnVpnPackageForUser(UserHandle.myUserId()); // Can't prepare new vpn app when another vpn is always-on if (alwaysOnVpnPackage != null && !alwaysOnVpnPackage.equals(mPackage)) { finish(); diff --git a/packages/overlays/AccentColorAmethystOverlay/Android.bp b/packages/overlays/AccentColorAmethystOverlay/Android.bp index 7519b12dd2d9..186d770c09a5 100644 --- a/packages/overlays/AccentColorAmethystOverlay/Android.bp +++ b/packages/overlays/AccentColorAmethystOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "AccentColorAmethystOverlay", theme: "AccentColorAmethyst", diff --git a/packages/overlays/AccentColorAquamarineOverlay/Android.bp b/packages/overlays/AccentColorAquamarineOverlay/Android.bp index 4469b36cfbc5..7fd64f374522 100644 --- a/packages/overlays/AccentColorAquamarineOverlay/Android.bp +++ b/packages/overlays/AccentColorAquamarineOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "AccentColorAquamarineOverlay", theme: "AccentColorAquamarine", diff --git a/packages/overlays/AccentColorBlackOverlay/Android.bp b/packages/overlays/AccentColorBlackOverlay/Android.bp index bfee26ea52dd..ac923ebd7cd9 100644 --- a/packages/overlays/AccentColorBlackOverlay/Android.bp +++ b/packages/overlays/AccentColorBlackOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "AccentColorBlackOverlay", theme: "AccentColorBlack", diff --git a/packages/overlays/AccentColorCarbonOverlay/Android.bp b/packages/overlays/AccentColorCarbonOverlay/Android.bp index 47f66dd9ba62..f4f1b8b50a1e 100644 --- a/packages/overlays/AccentColorCarbonOverlay/Android.bp +++ b/packages/overlays/AccentColorCarbonOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "AccentColorCarbonOverlay", theme: "AccentColorCarbon", diff --git a/packages/overlays/AccentColorCinnamonOverlay/Android.bp b/packages/overlays/AccentColorCinnamonOverlay/Android.bp index 8250315256b8..53899bfefd98 100644 --- a/packages/overlays/AccentColorCinnamonOverlay/Android.bp +++ b/packages/overlays/AccentColorCinnamonOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "AccentColorCinnamonOverlay", theme: "AccentColorCinnamon", diff --git a/packages/overlays/AccentColorGreenOverlay/Android.bp b/packages/overlays/AccentColorGreenOverlay/Android.bp index 15b50c76aa38..5b1f7447a7ca 100644 --- a/packages/overlays/AccentColorGreenOverlay/Android.bp +++ b/packages/overlays/AccentColorGreenOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "AccentColorGreenOverlay", theme: "AccentColorGreen", diff --git a/packages/overlays/AccentColorOceanOverlay/Android.bp b/packages/overlays/AccentColorOceanOverlay/Android.bp index 6ad63bc92816..a85883044dc2 100644 --- a/packages/overlays/AccentColorOceanOverlay/Android.bp +++ b/packages/overlays/AccentColorOceanOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "AccentColorOceanOverlay", theme: "AccentColorOcean", diff --git a/packages/overlays/AccentColorOrchidOverlay/Android.bp b/packages/overlays/AccentColorOrchidOverlay/Android.bp index b66933397e12..31ed30921664 100644 --- a/packages/overlays/AccentColorOrchidOverlay/Android.bp +++ b/packages/overlays/AccentColorOrchidOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "AccentColorOrchidOverlay", theme: "AccentColorOrchid", diff --git a/packages/overlays/AccentColorPaletteOverlay/Android.bp b/packages/overlays/AccentColorPaletteOverlay/Android.bp index eeefd1623a49..a6cc1dec37dd 100644 --- a/packages/overlays/AccentColorPaletteOverlay/Android.bp +++ b/packages/overlays/AccentColorPaletteOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "AccentColorPaletteOverlay", theme: "AccentColorPalette", diff --git a/packages/overlays/AccentColorPurpleOverlay/Android.bp b/packages/overlays/AccentColorPurpleOverlay/Android.bp index ead95df18d9e..80e0ab1159b7 100644 --- a/packages/overlays/AccentColorPurpleOverlay/Android.bp +++ b/packages/overlays/AccentColorPurpleOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "AccentColorPurpleOverlay", theme: "AccentColorPurple", diff --git a/packages/overlays/AccentColorSandOverlay/Android.bp b/packages/overlays/AccentColorSandOverlay/Android.bp index f70578a1ec38..771abca4c3f6 100644 --- a/packages/overlays/AccentColorSandOverlay/Android.bp +++ b/packages/overlays/AccentColorSandOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "AccentColorSandOverlay", theme: "AccentColorSand", diff --git a/packages/overlays/AccentColorSpaceOverlay/Android.bp b/packages/overlays/AccentColorSpaceOverlay/Android.bp index 1d713df3cfdd..8e4abacf1ef2 100644 --- a/packages/overlays/AccentColorSpaceOverlay/Android.bp +++ b/packages/overlays/AccentColorSpaceOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "AccentColorSpaceOverlay", theme: "AccentColorSpace", diff --git a/packages/overlays/AccentColorTangerineOverlay/Android.bp b/packages/overlays/AccentColorTangerineOverlay/Android.bp index d3b1e54fe823..75c708ec9fe7 100644 --- a/packages/overlays/AccentColorTangerineOverlay/Android.bp +++ b/packages/overlays/AccentColorTangerineOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "AccentColorTangerineOverlay", theme: "AccentColorTangerine", diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.bp index b8def98791be..8e03809cadf0 100644 --- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.bp +++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "DisplayCutoutEmulationCornerOverlay", theme: "DisplayCutoutEmulationCorner", diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.bp index b64ddfd82c05..afa5b649a432 100644 --- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.bp +++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "DisplayCutoutEmulationDoubleOverlay", theme: "DisplayCutoutEmulationDouble", diff --git a/packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.bp index 86cfebfbd0db..eae907db00ff 100644 --- a/packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.bp +++ b/packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "DisplayCutoutEmulationHoleOverlay", theme: "DisplayCutoutEmulationHole", diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.bp index 28ad9db119cb..25bc676bb027 100644 --- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.bp +++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "DisplayCutoutEmulationNarrowOverlay", theme: "DisplayCutoutEmulationNarrow", diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.bp index cd00386658ed..2828612254a3 100644 --- a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.bp +++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "DisplayCutoutEmulationTallOverlay", theme: "DisplayCutoutEmulationTall", diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.bp index d5fe683d5e55..66be777649ed 100644 --- a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.bp +++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "DisplayCutoutEmulationWaterfallOverlay", theme: "DisplayCutoutEmulationWaterfall", diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.bp index 0157ec48ee3a..e71cefe7643e 100644 --- a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.bp +++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "DisplayCutoutEmulationWideOverlay", theme: "DisplayCutoutEmulationWide", diff --git a/packages/overlays/FontNotoSerifSourceOverlay/Android.bp b/packages/overlays/FontNotoSerifSourceOverlay/Android.bp index 7fd145b57934..231295b41a27 100644 --- a/packages/overlays/FontNotoSerifSourceOverlay/Android.bp +++ b/packages/overlays/FontNotoSerifSourceOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "FontNotoSerifSourceOverlay", theme: "FontNotoSerifSource", diff --git a/packages/overlays/IconPackCircularAndroidOverlay/Android.bp b/packages/overlays/IconPackCircularAndroidOverlay/Android.bp index cd5829aae3b6..70403588da33 100644 --- a/packages/overlays/IconPackCircularAndroidOverlay/Android.bp +++ b/packages/overlays/IconPackCircularAndroidOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackCircularAndroidOverlay", theme: "IconPackCircularAndroid", diff --git a/packages/overlays/IconPackCircularLauncherOverlay/Android.bp b/packages/overlays/IconPackCircularLauncherOverlay/Android.bp index 5f2491d3cd66..4f8b6637a2b5 100644 --- a/packages/overlays/IconPackCircularLauncherOverlay/Android.bp +++ b/packages/overlays/IconPackCircularLauncherOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackCircularLauncherOverlay", theme: "IconPackCircularLauncher", diff --git a/packages/overlays/IconPackCircularSettingsOverlay/Android.bp b/packages/overlays/IconPackCircularSettingsOverlay/Android.bp index d7bc6573e986..93220c87dcf9 100644 --- a/packages/overlays/IconPackCircularSettingsOverlay/Android.bp +++ b/packages/overlays/IconPackCircularSettingsOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackCircularSettingsOverlay", theme: "IconPackCircularSettings", diff --git a/packages/overlays/IconPackCircularSystemUIOverlay/Android.bp b/packages/overlays/IconPackCircularSystemUIOverlay/Android.bp index 73b8cd8e7d5f..4eaa4205fe96 100644 --- a/packages/overlays/IconPackCircularSystemUIOverlay/Android.bp +++ b/packages/overlays/IconPackCircularSystemUIOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackCircularSystemUIOverlay", theme: "IconPackCircularSystemUI", diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/Android.bp b/packages/overlays/IconPackCircularThemePickerOverlay/Android.bp index 50639322607e..5105b7931922 100644 --- a/packages/overlays/IconPackCircularThemePickerOverlay/Android.bp +++ b/packages/overlays/IconPackCircularThemePickerOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackCircularThemePickerOverlay", theme: "IconPackCircularThemePicker", diff --git a/packages/overlays/IconPackFilledAndroidOverlay/Android.bp b/packages/overlays/IconPackFilledAndroidOverlay/Android.bp index 83f365678caf..3c4025d6026c 100644 --- a/packages/overlays/IconPackFilledAndroidOverlay/Android.bp +++ b/packages/overlays/IconPackFilledAndroidOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackFilledAndroidOverlay", theme: "IconPackFilledAndroid", diff --git a/packages/overlays/IconPackFilledLauncherOverlay/Android.bp b/packages/overlays/IconPackFilledLauncherOverlay/Android.bp index 6ca256638a03..3c5078ce5933 100644 --- a/packages/overlays/IconPackFilledLauncherOverlay/Android.bp +++ b/packages/overlays/IconPackFilledLauncherOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackFilledLauncherOverlay", theme: "IconPackFilledLauncher", diff --git a/packages/overlays/IconPackFilledSettingsOverlay/Android.bp b/packages/overlays/IconPackFilledSettingsOverlay/Android.bp index 8551bd54d588..b5148c23e053 100644 --- a/packages/overlays/IconPackFilledSettingsOverlay/Android.bp +++ b/packages/overlays/IconPackFilledSettingsOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackFilledSettingsOverlay", theme: "IconPackFilledSettings", diff --git a/packages/overlays/IconPackFilledSystemUIOverlay/Android.bp b/packages/overlays/IconPackFilledSystemUIOverlay/Android.bp index 684deb453d74..eb040a5a132d 100644 --- a/packages/overlays/IconPackFilledSystemUIOverlay/Android.bp +++ b/packages/overlays/IconPackFilledSystemUIOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackFilledSystemUIOverlay", theme: "IconPackFilledSystemUI", diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/Android.bp b/packages/overlays/IconPackFilledThemePickerOverlay/Android.bp index dae378f67774..bee48089109b 100644 --- a/packages/overlays/IconPackFilledThemePickerOverlay/Android.bp +++ b/packages/overlays/IconPackFilledThemePickerOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackFilledThemePickerOverlay", theme: "IconPackFilledThemePicker", diff --git a/packages/overlays/IconPackKaiAndroidOverlay/Android.bp b/packages/overlays/IconPackKaiAndroidOverlay/Android.bp index 4161e252c312..ee588c1f1c55 100644 --- a/packages/overlays/IconPackKaiAndroidOverlay/Android.bp +++ b/packages/overlays/IconPackKaiAndroidOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackKaiAndroidOverlay", theme: "IconPackKaiAndroid", diff --git a/packages/overlays/IconPackKaiLauncherOverlay/Android.bp b/packages/overlays/IconPackKaiLauncherOverlay/Android.bp index 4bf8f11a8475..dcdad7aaed4e 100644 --- a/packages/overlays/IconPackKaiLauncherOverlay/Android.bp +++ b/packages/overlays/IconPackKaiLauncherOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackKaiLauncherOverlay", theme: "IconPackKaiLauncher", diff --git a/packages/overlays/IconPackKaiSettingsOverlay/Android.bp b/packages/overlays/IconPackKaiSettingsOverlay/Android.bp index c43f9e58a0c9..974bb540f4e7 100644 --- a/packages/overlays/IconPackKaiSettingsOverlay/Android.bp +++ b/packages/overlays/IconPackKaiSettingsOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackKaiSettingsOverlay", theme: "IconPackKaiSettings", diff --git a/packages/overlays/IconPackKaiSystemUIOverlay/Android.bp b/packages/overlays/IconPackKaiSystemUIOverlay/Android.bp index 22a8c2843956..b04ca6132c6d 100644 --- a/packages/overlays/IconPackKaiSystemUIOverlay/Android.bp +++ b/packages/overlays/IconPackKaiSystemUIOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackKaiSystemUIOverlay", theme: "IconPackKaiSystemUI", diff --git a/packages/overlays/IconPackKaiThemePickerOverlay/Android.bp b/packages/overlays/IconPackKaiThemePickerOverlay/Android.bp index 85eb1c46272f..875cd1d44d92 100644 --- a/packages/overlays/IconPackKaiThemePickerOverlay/Android.bp +++ b/packages/overlays/IconPackKaiThemePickerOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackKaiThemePickerOverlay", theme: "IconPackKaiThemePicker", diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/Android.bp b/packages/overlays/IconPackRoundedAndroidOverlay/Android.bp index 948f015cabec..cb7b01361da3 100644 --- a/packages/overlays/IconPackRoundedAndroidOverlay/Android.bp +++ b/packages/overlays/IconPackRoundedAndroidOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackRoundedAndroidOverlay", theme: "IconPackRoundedAndroid", diff --git a/packages/overlays/IconPackRoundedLauncherOverlay/Android.bp b/packages/overlays/IconPackRoundedLauncherOverlay/Android.bp index 5fbe6352f889..8ab6d957720e 100644 --- a/packages/overlays/IconPackRoundedLauncherOverlay/Android.bp +++ b/packages/overlays/IconPackRoundedLauncherOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackRoundedLauncherOverlay", theme: "IconPackRoundedLauncher", diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/Android.bp b/packages/overlays/IconPackRoundedSettingsOverlay/Android.bp index b1a07137eaa2..ee2f98a96864 100644 --- a/packages/overlays/IconPackRoundedSettingsOverlay/Android.bp +++ b/packages/overlays/IconPackRoundedSettingsOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackRoundedSettingsOverlay", theme: "IconPackRoundedSettings", diff --git a/packages/overlays/IconPackRoundedSystemUIOverlay/Android.bp b/packages/overlays/IconPackRoundedSystemUIOverlay/Android.bp index 643299898169..ee0220a44943 100644 --- a/packages/overlays/IconPackRoundedSystemUIOverlay/Android.bp +++ b/packages/overlays/IconPackRoundedSystemUIOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackRoundedSystemUIOverlay", theme: "IconPackRoundedSystemUI", diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/Android.bp b/packages/overlays/IconPackRoundedThemePickerOverlay/Android.bp index 95e1d3a2ac6c..d74765c33607 100644 --- a/packages/overlays/IconPackRoundedThemePickerOverlay/Android.bp +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackRoundedThemePickerOverlay", theme: "IconPackRoundedTheme", diff --git a/packages/overlays/IconPackSamAndroidOverlay/Android.bp b/packages/overlays/IconPackSamAndroidOverlay/Android.bp index e8c33b10fb9e..2e9dc3472674 100644 --- a/packages/overlays/IconPackSamAndroidOverlay/Android.bp +++ b/packages/overlays/IconPackSamAndroidOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackSamAndroidOverlay", theme: "IconPackSamAndroid", diff --git a/packages/overlays/IconPackSamLauncherOverlay/Android.bp b/packages/overlays/IconPackSamLauncherOverlay/Android.bp index a46964665862..aa0cf0077ab9 100644 --- a/packages/overlays/IconPackSamLauncherOverlay/Android.bp +++ b/packages/overlays/IconPackSamLauncherOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackSamLauncherOverlay", theme: "IconPackSamLauncher", diff --git a/packages/overlays/IconPackSamSettingsOverlay/Android.bp b/packages/overlays/IconPackSamSettingsOverlay/Android.bp index bc1fa458ad9a..a62037f3d5c2 100644 --- a/packages/overlays/IconPackSamSettingsOverlay/Android.bp +++ b/packages/overlays/IconPackSamSettingsOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackSamSettingsOverlay", theme: "IconPackSamSettings", diff --git a/packages/overlays/IconPackSamSystemUIOverlay/Android.bp b/packages/overlays/IconPackSamSystemUIOverlay/Android.bp index db77352f42b9..96ba7a096e6a 100644 --- a/packages/overlays/IconPackSamSystemUIOverlay/Android.bp +++ b/packages/overlays/IconPackSamSystemUIOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackSamSystemUIOverlay", theme: "IconPackSamSystemUI", diff --git a/packages/overlays/IconPackSamThemePickerOverlay/Android.bp b/packages/overlays/IconPackSamThemePickerOverlay/Android.bp index c216868a2a5e..7376f03a2c7f 100644 --- a/packages/overlays/IconPackSamThemePickerOverlay/Android.bp +++ b/packages/overlays/IconPackSamThemePickerOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackSamThemePickerOverlay", theme: "IconPackSamThemePicker", diff --git a/packages/overlays/IconPackVictorAndroidOverlay/Android.bp b/packages/overlays/IconPackVictorAndroidOverlay/Android.bp index de62af1c6287..ee7377863287 100644 --- a/packages/overlays/IconPackVictorAndroidOverlay/Android.bp +++ b/packages/overlays/IconPackVictorAndroidOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackVictorAndroidOverlay", theme: "IconPackVictorAndroid", diff --git a/packages/overlays/IconPackVictorLauncherOverlay/Android.bp b/packages/overlays/IconPackVictorLauncherOverlay/Android.bp index fc4c3606de8e..a0cd45a81e20 100644 --- a/packages/overlays/IconPackVictorLauncherOverlay/Android.bp +++ b/packages/overlays/IconPackVictorLauncherOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackVictorLauncherOverlay", theme: "IconPackVictorLauncher", diff --git a/packages/overlays/IconPackVictorSettingsOverlay/Android.bp b/packages/overlays/IconPackVictorSettingsOverlay/Android.bp index 046bb3d25a12..7807c6bcccc8 100644 --- a/packages/overlays/IconPackVictorSettingsOverlay/Android.bp +++ b/packages/overlays/IconPackVictorSettingsOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackVictorSettingsOverlay", theme: "IconPackVictorSettings", diff --git a/packages/overlays/IconPackVictorSystemUIOverlay/Android.bp b/packages/overlays/IconPackVictorSystemUIOverlay/Android.bp index b8a9e77ebf7d..2deb6cd73ba1 100644 --- a/packages/overlays/IconPackVictorSystemUIOverlay/Android.bp +++ b/packages/overlays/IconPackVictorSystemUIOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackVictorSystemUIOverlay", theme: "IconPackVictorSystemUI", diff --git a/packages/overlays/IconPackVictorThemePickerOverlay/Android.bp b/packages/overlays/IconPackVictorThemePickerOverlay/Android.bp index 6f0144e9e351..a18ebb3eaea5 100644 --- a/packages/overlays/IconPackVictorThemePickerOverlay/Android.bp +++ b/packages/overlays/IconPackVictorThemePickerOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconPackVictorThemePickerOverlay", theme: "IconPackVictorThemePicker", diff --git a/packages/overlays/IconShapeHeartOverlay/Android.bp b/packages/overlays/IconShapeHeartOverlay/Android.bp index ec55712f2309..1da8f4ff7fb3 100644 --- a/packages/overlays/IconShapeHeartOverlay/Android.bp +++ b/packages/overlays/IconShapeHeartOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconShapeHeartOverlay", theme: "IconShapeHeart", diff --git a/packages/overlays/IconShapePebbleOverlay/Android.bp b/packages/overlays/IconShapePebbleOverlay/Android.bp index 7dc4fde140c8..fa2a5bb825f3 100644 --- a/packages/overlays/IconShapePebbleOverlay/Android.bp +++ b/packages/overlays/IconShapePebbleOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconShapePebbleOverlay", theme: "IconShapePebble", diff --git a/packages/overlays/IconShapeRoundedRectOverlay/Android.bp b/packages/overlays/IconShapeRoundedRectOverlay/Android.bp index b8b85314a211..5052d08f1edf 100644 --- a/packages/overlays/IconShapeRoundedRectOverlay/Android.bp +++ b/packages/overlays/IconShapeRoundedRectOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconShapeRoundedRectOverlay", theme: "IconShapeRoundedRect", diff --git a/packages/overlays/IconShapeSquareOverlay/Android.bp b/packages/overlays/IconShapeSquareOverlay/Android.bp index fdeffeee25d2..1176abddd71f 100644 --- a/packages/overlays/IconShapeSquareOverlay/Android.bp +++ b/packages/overlays/IconShapeSquareOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconShapeSquareOverlay", theme: "IconShapeSquare", diff --git a/packages/overlays/IconShapeSquircleOverlay/Android.bp b/packages/overlays/IconShapeSquircleOverlay/Android.bp index 468f0f7ee2c7..8c219f340040 100644 --- a/packages/overlays/IconShapeSquircleOverlay/Android.bp +++ b/packages/overlays/IconShapeSquircleOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconShapeSquircleOverlay", theme: "IconShapeSquircle", diff --git a/packages/overlays/IconShapeTaperedRectOverlay/Android.bp b/packages/overlays/IconShapeTaperedRectOverlay/Android.bp index 1e48cd154317..78855e8daba2 100644 --- a/packages/overlays/IconShapeTaperedRectOverlay/Android.bp +++ b/packages/overlays/IconShapeTaperedRectOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconShapeTaperedRectOverlay", theme: "IconShapeTaperedRect", diff --git a/packages/overlays/IconShapeTeardropOverlay/Android.bp b/packages/overlays/IconShapeTeardropOverlay/Android.bp index 017d58ec4e77..dd36f4ffc995 100644 --- a/packages/overlays/IconShapeTeardropOverlay/Android.bp +++ b/packages/overlays/IconShapeTeardropOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconShapeTeardropOverlay", theme: "IconShapeTeardrop", diff --git a/packages/overlays/IconShapeVesselOverlay/Android.bp b/packages/overlays/IconShapeVesselOverlay/Android.bp index ba3b30954e7f..2e7f8bc6cf66 100644 --- a/packages/overlays/IconShapeVesselOverlay/Android.bp +++ b/packages/overlays/IconShapeVesselOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "IconShapeVesselOverlay", theme: "IconShapeVessel", diff --git a/packages/overlays/NavigationBarMode2ButtonOverlay/Android.bp b/packages/overlays/NavigationBarMode2ButtonOverlay/Android.bp index e4fcce15250f..35f671bab875 100644 --- a/packages/overlays/NavigationBarMode2ButtonOverlay/Android.bp +++ b/packages/overlays/NavigationBarMode2ButtonOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "NavigationBarMode2ButtonOverlay", theme: "NavigationBarMode2Button", diff --git a/packages/overlays/NavigationBarMode3ButtonOverlay/Android.bp b/packages/overlays/NavigationBarMode3ButtonOverlay/Android.bp index 9b6c9988b9bb..fe9cc81f0d52 100644 --- a/packages/overlays/NavigationBarMode3ButtonOverlay/Android.bp +++ b/packages/overlays/NavigationBarMode3ButtonOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "NavigationBarMode3ButtonOverlay", theme: "NavigationBarMode3Button", diff --git a/packages/overlays/NavigationBarModeGesturalOverlay/Android.bp b/packages/overlays/NavigationBarModeGesturalOverlay/Android.bp index ba290459a279..791f4205481f 100644 --- a/packages/overlays/NavigationBarModeGesturalOverlay/Android.bp +++ b/packages/overlays/NavigationBarModeGesturalOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "NavigationBarModeGesturalOverlay", theme: "NavigationBarModeGestural", diff --git a/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/Android.bp b/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/Android.bp index 0c688a988533..28f9f3341307 100644 --- a/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/Android.bp +++ b/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "NavigationBarModeGesturalOverlayExtraWideBack", theme: "NavigationBarModeGesturalExtraWideBack", diff --git a/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/Android.bp b/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/Android.bp index 85d514f7fbac..f8a56030e0e4 100644 --- a/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/Android.bp +++ b/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "NavigationBarModeGesturalOverlayNarrowBack", theme: "NavigationBarModeGesturalNarrowBack", diff --git a/packages/overlays/NavigationBarModeGesturalOverlayWideBack/Android.bp b/packages/overlays/NavigationBarModeGesturalOverlayWideBack/Android.bp index 84be626eefec..60ee6d540dc8 100644 --- a/packages/overlays/NavigationBarModeGesturalOverlayWideBack/Android.bp +++ b/packages/overlays/NavigationBarModeGesturalOverlayWideBack/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "NavigationBarModeGesturalOverlayWideBack", theme: "NavigationBarModeGesturalWideBack", diff --git a/packages/overlays/OneHandedModeGesturalOverlay/Android.bp b/packages/overlays/OneHandedModeGesturalOverlay/Android.bp index 9c9d0ef380a7..468069dd8334 100644 --- a/packages/overlays/OneHandedModeGesturalOverlay/Android.bp +++ b/packages/overlays/OneHandedModeGesturalOverlay/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + runtime_resource_overlay { name: "OneHandedModeGesturalOverlay", theme: "OneHandedModeGestural", diff --git a/packages/services/CameraExtensionsProxy/Android.bp b/packages/services/CameraExtensionsProxy/Android.bp index e2e4af2a35f4..ea0703e2bc24 100644 --- a/packages/services/CameraExtensionsProxy/Android.bp +++ b/packages/services/CameraExtensionsProxy/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_app { name: "CameraExtensionsProxy", srcs: ["src/**/*.java"], diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index ea1473ea3db7..c63c2e1a257d 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -777,6 +777,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // performs the current profile parent resolution. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked(userId); + + if (Binder.getCallingPid() == OWN_PROCESS_ID) { + return new ArrayList<>(getUserStateLocked(resolvedUserId).mInstalledServices); + } return getUserStateLocked(resolvedUserId).mInstalledServices; } } diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 809304bb24ae..7518c7a8bdc9 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -2673,7 +2673,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN); info.widgetFeatures = sa.getInt( com.android.internal.R.styleable.AppWidgetProviderInfo_widgetFeatures, 0); - info.descriptionResource = sa.getResourceId( + info.descriptionRes = sa.getResourceId( com.android.internal.R.styleable.AppWidgetProviderInfo_description, Resources.ID_NULL); sa.recycle(); diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 6c30999f63a4..38275f7cd348 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -1245,10 +1245,9 @@ public class BackupManagerService extends IBackupManager.Stub { @Override public IRestoreSession beginRestoreSessionForUser( - int userId, String packageName, String transportID, - @OperationType int operationType) throws RemoteException { + int userId, String packageName, String transportID) throws RemoteException { return isUserReadyForBackup(userId) - ? beginRestoreSession(userId, packageName, transportID, operationType) : null; + ? beginRestoreSession(userId, packageName, transportID) : null; } /** @@ -1257,15 +1256,13 @@ public class BackupManagerService extends IBackupManager.Stub { */ @Nullable public IRestoreSession beginRestoreSession( - @UserIdInt int userId, String packageName, String transportName, - @OperationType int operationType) { + @UserIdInt int userId, String packageName, String transportName) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "beginRestoreSession()"); return userBackupManagerService == null ? null - : userBackupManagerService.beginRestoreSession(packageName, transportName, - operationType); + : userBackupManagerService.beginRestoreSession(packageName, transportName); } @Override @@ -1350,15 +1347,15 @@ public class BackupManagerService extends IBackupManager.Stub { if (!isUserReadyForBackup(userId)) { return BackupManager.ERROR_BACKUP_NOT_ALLOWED; } - return requestBackup(userId, packages, observer, monitor, flags, OperationType.BACKUP); + return requestBackup(userId, packages, observer, monitor, flags); } @Override public int requestBackup(String[] packages, IBackupObserver observer, - IBackupManagerMonitor monitor, int flags, @OperationType int operationType) + IBackupManagerMonitor monitor, int flags) throws RemoteException { return requestBackup(binderGetCallingUserId(), packages, - observer, monitor, flags, operationType); + observer, monitor, flags); } /** @@ -1370,15 +1367,13 @@ public class BackupManagerService extends IBackupManager.Stub { String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor, - int flags, - @OperationType int operationType) { + int flags) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "requestBackup()"); return userBackupManagerService == null ? BackupManager.ERROR_BACKUP_NOT_ALLOWED - : userBackupManagerService.requestBackup(packages, observer, monitor, flags, - operationType); + : userBackupManagerService.requestBackup(packages, observer, monitor, flags); } @Override diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index 136cd22fad83..9ee0159e903a 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -96,6 +96,7 @@ import android.text.TextUtils; import android.util.ArraySet; import android.util.AtomicFile; import android.util.EventLog; +import android.util.FeatureFlagUtils; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; @@ -127,6 +128,7 @@ import com.android.server.backup.params.RestoreParams; import com.android.server.backup.restore.ActiveRestoreSession; import com.android.server.backup.restore.PerformUnifiedRestoreTask; import com.android.server.backup.transport.TransportClient; +import com.android.server.backup.transport.TransportNotAvailableException; import com.android.server.backup.transport.TransportNotRegisteredException; import com.android.server.backup.utils.BackupEligibilityRules; import com.android.server.backup.utils.BackupManagerMonitorUtils; @@ -1860,19 +1862,10 @@ public class UserBackupManagerService { /** * Requests a backup for the inputted {@code packages} with a specified {@link - * IBackupManagerMonitor}. - */ - public int requestBackup(String[] packages, IBackupObserver observer, - IBackupManagerMonitor monitor, int flags) { - return requestBackup(packages, observer, monitor, flags, OperationType.BACKUP); - } - - /** - * Requests a backup for the inputted {@code packages} with a specified {@link * IBackupManagerMonitor} and {@link OperationType}. */ public int requestBackup(String[] packages, IBackupObserver observer, - IBackupManagerMonitor monitor, int flags, @OperationType int operationType) { + IBackupManagerMonitor monitor, int flags) { mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup"); if (packages == null || packages.length < 1) { @@ -1903,13 +1896,16 @@ public class UserBackupManagerService { final TransportClient transportClient; final String transportDirName; + int operationType; try { transportDirName = mTransportManager.getTransportDirName( mTransportManager.getCurrentTransportName()); transportClient = mTransportManager.getCurrentTransportClientOrThrow("BMS.requestBackup()"); - } catch (TransportNotRegisteredException e) { + operationType = getOperationTypeFromTransport(transportClient); + } catch (TransportNotRegisteredException | TransportNotAvailableException + | RemoteException e) { BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED); monitor = BackupManagerMonitorUtils.monitorEvent(monitor, BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL, @@ -4024,15 +4020,13 @@ public class UserBackupManagerService { } /** Hand off a restore session. */ - public IRestoreSession beginRestoreSession(String packageName, String transport, - @OperationType int operationType) { + public IRestoreSession beginRestoreSession(String packageName, String transport) { if (DEBUG) { Slog.v( TAG, addUserIdToLogMessage( mUserId, - "beginRestoreSession: pkg=" + packageName + " transport=" + transport - + "operationType=" + operationType)); + "beginRestoreSession: pkg=" + packageName + " transport=" + transport)); } boolean needPermission = true; @@ -4073,6 +4067,17 @@ public class UserBackupManagerService { } } + int operationType; + try { + operationType = getOperationTypeFromTransport( + mTransportManager.getTransportClientOrThrow(transport, /* caller */ + "BMS.beginRestoreSession")); + } catch (TransportNotAvailableException | TransportNotRegisteredException + | RemoteException e) { + Slog.w(TAG, "Failed to get operation type from transport: " + e); + return null; + } + synchronized (this) { if (mActiveRestoreSession != null) { Slog.i( @@ -4356,6 +4361,34 @@ public class UserBackupManagerService { } } + @VisibleForTesting + @OperationType int getOperationTypeFromTransport(TransportClient transportClient) + throws TransportNotAvailableException, RemoteException { + if (!shouldUseNewBackupEligibilityRules()) { + // Return the default to stick to the legacy behaviour. + return OperationType.BACKUP; + } + + long oldCallingId = Binder.clearCallingIdentity(); + try { + IBackupTransport transport = transportClient.connectOrThrow( + /* caller */ "BMS.getOperationTypeFromTransport"); + if ((transport.getTransportFlags() & BackupAgent.FLAG_DEVICE_TO_DEVICE_TRANSFER) != 0) { + return OperationType.MIGRATION; + } else { + return OperationType.BACKUP; + } + } finally { + Binder.restoreCallingIdentity(oldCallingId); + } + } + + @VisibleForTesting + boolean shouldUseNewBackupEligibilityRules() { + return FeatureFlagUtils.isEnabled(mContext, + FeatureFlagUtils.SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES); + } + private static String addUserIdToLogMessage(int userId, String message) { return "[UserID:" + userId + "] " + message; } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 76c8d3001158..ebfa9171b2b9 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -142,18 +142,13 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Function; -//TODO onStop schedule unbind in 5 seconds -//TODO make sure APIs are only callable from currently focused app -//TODO schedule stopScan on activity destroy(except if configuration change) -//TODO on associate called again after configuration change -> replace old callback with new -//TODO avoid leaking calling activity in IFindDeviceCallback (see PrintManager#print for example) /** @hide */ @SuppressLint("LongLogTag") public class CompanionDeviceManagerService extends SystemService implements Binder.DeathRecipient { private static final ComponentName SERVICE_TO_BIND_TO = ComponentName.createRelative( CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, - ".DeviceDiscoveryService"); + ".CompanionDeviceDiscoveryService"); private static final long DEVICE_DISAPPEARED_TIMEOUT_MS = 10 * 1000; private static final long DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS = 10 * 60 * 1000; @@ -1422,7 +1417,9 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } @Override - public void onDeviceDisconnected(BluetoothDevice device) { + public void onDeviceDisconnected(BluetoothDevice device, @DisconnectReason int reason) { + Slog.d(LOG_TAG, device.getAddress() + " disconnected w/ reason: (" + reason + ") " + + BluetoothAdapter.BluetoothConnectionCallback.disconnectReasonText(reason)); CompanionDeviceManagerService.this.onDeviceDisconnected(device.getAddress()); } } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 9d86f4eaa520..f4138d10a84d 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -188,14 +188,16 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.logging.MetricsLogger; -import com.android.internal.util.ArrayUtils; import com.android.internal.util.AsyncChannel; +import com.android.internal.util.BitUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.LocationPermissionChecker; import com.android.internal.util.MessageUtils; import com.android.modules.utils.BasicShellCommandHandler; +import com.android.net.module.util.CollectionUtils; import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult; import com.android.net.module.util.LinkPropertiesUtils.CompareResult; +import com.android.net.module.util.PermissionUtils; import com.android.server.am.BatteryStatsService; import com.android.server.connectivity.AutodestructReference; import com.android.server.connectivity.DataConnectionStats; @@ -1510,7 +1512,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public Network getActiveNetworkForUid(int uid, boolean ignoreBlocked) { - NetworkStack.checkNetworkStackPermission(mContext); + PermissionUtils.enforceNetworkStackPermission(mContext); return getActiveNetworkForUidInternal(uid, ignoreBlocked); } @@ -1533,7 +1535,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkInfo getActiveNetworkInfoForUid(int uid, boolean ignoreBlocked) { - NetworkStack.checkNetworkStackPermission(mContext); + PermissionUtils.enforceNetworkStackPermission(mContext); final NetworkState state = getUnfilteredActiveNetworkState(uid); filterNetworkStateForUid(state, uid, ignoreBlocked); return state.networkInfo; @@ -1877,7 +1879,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkState[] getAllNetworkState() { // This contains IMSI details, so make sure the caller is privileged. - NetworkStack.checkNetworkStackPermission(mContext); + PermissionUtils.enforceNetworkStackPermission(mContext); final ArrayList<NetworkState> result = new ArrayList<>(); for (Network network : getAllNetworks()) { @@ -2301,7 +2303,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // Public because it's used by mLockdownTracker. public void sendConnectedBroadcast(NetworkInfo info) { - NetworkStack.checkNetworkStackPermission(mContext); + PermissionUtils.enforceNetworkStackPermission(mContext); sendGeneralBroadcast(info, CONNECTIVITY_ACTION); } @@ -2565,13 +2567,13 @@ public class ConnectivityService extends IConnectivityManager.Stub if (!checkDumpPermission(mContext, TAG, pw)) return; if (asProto) return; - if (ArrayUtils.contains(args, DIAG_ARG)) { + if (CollectionUtils.contains(args, DIAG_ARG)) { dumpNetworkDiagnostics(pw); return; - } else if (ArrayUtils.contains(args, NETWORK_ARG)) { + } else if (CollectionUtils.contains(args, NETWORK_ARG)) { dumpNetworks(pw); return; - } else if (ArrayUtils.contains(args, REQUEST_ARG)) { + } else if (CollectionUtils.contains(args, REQUEST_ARG)) { dumpNetworkRequests(pw); return; } @@ -2642,7 +2644,7 @@ public class ConnectivityService extends IConnectivityManager.Stub pw.println(); - if (ArrayUtils.contains(args, SHORT_ARG) == false) { + if (!CollectionUtils.contains(args, SHORT_ARG)) { pw.println(); pw.println("mNetworkRequestInfoLogs (most recent first):"); pw.increaseIndent(); @@ -4684,7 +4686,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public void setGlobalProxy(final ProxyInfo proxyProperties) { - NetworkStack.checkNetworkStackPermission(mContext); + PermissionUtils.enforceNetworkStackPermission(mContext); mProxyTracker.setGlobalProxy(proxyProperties); } @@ -4809,7 +4811,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - if (ArrayUtils.isEmpty(underlyingNetworks)) return null; + if (CollectionUtils.isEmpty(underlyingNetworks)) return null; List<String> interfaces = new ArrayList<>(); for (Network network : underlyingNetworks) { @@ -4853,7 +4855,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (!nai.supportsUnderlyingNetworks()) return false; final Network[] underlying = underlyingNetworksOrDefault( nai.networkCapabilities.getOwnerUid(), nai.declaredUnderlyingNetworks); - return ArrayUtils.contains(underlying, network); + return CollectionUtils.contains(underlying, network); } /** @@ -4886,7 +4888,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public void setRequireVpnForUids(boolean requireVpn, UidRange[] ranges) { - NetworkStack.checkNetworkStackPermission(mContext); + PermissionUtils.enforceNetworkStackPermission(mContext); mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_REQUIRE_VPN_FOR_UIDS, encodeBool(requireVpn), 0 /* arg2 */, ranges)); } @@ -5317,8 +5319,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } } - // TODO: use NetworkStackUtils.convertToIntArray after moving it - return ArrayUtils.convertToIntArray(new ArrayList<>(thresholds)); + return CollectionUtils.toIntArray(new ArrayList<>(thresholds)); } private void updateSignalStrengthThresholds( @@ -6437,7 +6438,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @NonNull NetworkCapabilities agentCaps, @NonNull NetworkCapabilities newNc) { underlyingNetworks = underlyingNetworksOrDefault( agentCaps.getOwnerUid(), underlyingNetworks); - int[] transportTypes = agentCaps.getTransportTypes(); + long transportTypes = BitUtils.packBits(agentCaps.getTransportTypes()); int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; // metered if any underlying is metered, or originally declared metered by the agent. @@ -6456,7 +6457,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final NetworkCapabilities underlyingCaps = underlying.networkCapabilities; hadUnderlyingNetworks = true; for (int underlyingType : underlyingCaps.getTransportTypes()) { - transportTypes = ArrayUtils.appendInt(transportTypes, underlyingType); + transportTypes |= 1L << underlyingType; } // Merge capabilities of this underlying network. For bandwidth, assume the @@ -6487,7 +6488,7 @@ public class ConnectivityService extends IConnectivityManager.Stub suspended = false; } - newNc.setTransportTypes(transportTypes); + newNc.setTransportTypes(BitUtils.unpackBits(transportTypes)); newNc.setLinkDownstreamBandwidthKbps(downKbps); newNc.setLinkUpstreamBandwidthKbps(upKbps); newNc.setCapability(NET_CAPABILITY_NOT_METERED, !metered); @@ -8552,14 +8553,14 @@ public class ConnectivityService extends IConnectivityManager.Stub for (NetworkAgentInfo virtual : mNetworkAgentInfos) { if (virtual.supportsUnderlyingNetworks() && virtual.networkCapabilities.getOwnerUid() == callbackUid - && ArrayUtils.contains(virtual.declaredUnderlyingNetworks, nai.network)) { + && CollectionUtils.contains(virtual.declaredUnderlyingNetworks, nai.network)) { return true; } } // Administrator UIDs also contains the Owner UID final int[] administratorUids = nai.networkCapabilities.getAdministratorUids(); - return ArrayUtils.contains(administratorUids, callbackUid); + return CollectionUtils.contains(administratorUids, callbackUid); } @Override diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java index 88ce2208adcb..e29e894a5cc0 100644 --- a/services/core/java/com/android/server/DynamicSystemService.java +++ b/services/core/java/com/android/server/DynamicSystemService.java @@ -28,6 +28,7 @@ import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; import android.os.image.IDynamicSystemService; +import android.os.storage.DiskInfo; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; import android.util.Slog; @@ -40,6 +41,7 @@ import java.io.File; */ public class DynamicSystemService extends IDynamicSystemService.Stub { private static final String TAG = "DynamicSystemService"; + private static final long MINIMUM_SD_MB = (30L << 10); private static final int GSID_ROUGH_TIMEOUT_MS = 8192; private static final String PATH_DEFAULT = "/data/gsi/"; private Context mContext; @@ -95,6 +97,13 @@ public class DynamicSystemService extends IDynamicSystemService.Stub { if (!volume.isMountedWritable()) { continue; } + DiskInfo disk = volume.getDisk(); + long mega = disk.size >> 20; + Slog.i(TAG, volume.getPath() + ": " + mega + " MB"); + if (mega < MINIMUM_SD_MB) { + Slog.i(TAG, volume.getPath() + ": insufficient storage"); + continue; + } File sd_internal = volume.getInternalPathForUser(userId); if (sd_internal != null) { path = new File(sd_internal, dsuSlot).getPath(); diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index b48bc900aa84..81d4b9da63c8 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -48,7 +48,6 @@ import android.net.TrafficStats; import android.net.util.NetdService; import android.os.Binder; import android.os.IBinder; -import android.os.INetworkManagementService; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceSpecificException; @@ -64,6 +63,7 @@ import android.util.SparseBooleanArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; +import com.android.net.module.util.NetdUtils; import libcore.io.IoUtils; @@ -117,9 +117,6 @@ public class IpSecService extends IIpSecService.Stub { /* Binder context for this service */ private final Context mContext; - /* NetworkManager instance */ - private final INetworkManagementService mNetworkManager; - /** * The next non-repeating global ID for tracking resources between users, this service, and * kernel data structures. Accessing this variable is not thread safe, so it is only read or @@ -1014,13 +1011,13 @@ public class IpSecService extends IIpSecService.Stub { * * @param context Binder context for this service */ - private IpSecService(Context context, INetworkManagementService networkManager) { - this(context, networkManager, IpSecServiceConfiguration.GETSRVINSTANCE); + private IpSecService(Context context) { + this(context, IpSecServiceConfiguration.GETSRVINSTANCE); } - static IpSecService create(Context context, INetworkManagementService networkManager) + static IpSecService create(Context context) throws InterruptedException { - final IpSecService service = new IpSecService(context, networkManager); + final IpSecService service = new IpSecService(context); service.connectNativeNetdService(); return service; } @@ -1034,11 +1031,9 @@ public class IpSecService extends IIpSecService.Stub { /** @hide */ @VisibleForTesting - public IpSecService(Context context, INetworkManagementService networkManager, - IpSecServiceConfiguration config) { + public IpSecService(Context context, IpSecServiceConfiguration config) { this( context, - networkManager, config, (fd, uid) -> { try { @@ -1052,10 +1047,9 @@ public class IpSecService extends IIpSecService.Stub { /** @hide */ @VisibleForTesting - public IpSecService(Context context, INetworkManagementService networkManager, - IpSecServiceConfiguration config, UidFdTagger uidFdTagger) { + public IpSecService(Context context, IpSecServiceConfiguration config, + UidFdTagger uidFdTagger) { mContext = context; - mNetworkManager = Objects.requireNonNull(networkManager); mSrvConfig = config; mUidFdTagger = uidFdTagger; } @@ -1335,7 +1329,7 @@ public class IpSecService extends IIpSecService.Stub { netd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId); Binder.withCleanCallingIdentity(() -> { - mNetworkManager.setInterfaceUp(intfName); + NetdUtils.setInterfaceUp(netd, intfName); }); for (int selAddrFamily : ADDRESS_FAMILIES) { diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 329ab9983c90..8d5d3d939e4b 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -16,6 +16,8 @@ package com.android.server; +import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE; + import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback; @@ -837,7 +839,10 @@ public class VcnManagementService extends IVcnManagementService.Stub { // Notify all registered StatusCallbacks for this subGroup for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) { if (isCallbackPermissioned(cbInfo)) { - Binder.withCleanCallingIdentity(() -> cbInfo.mCallback.onEnteredSafeMode()); + Binder.withCleanCallingIdentity( + () -> + cbInfo.mCallback.onVcnStatusChanged( + VCN_STATUS_CODE_SAFE_MODE)); } } } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 5550999b2405..2efc83c4af06 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -52,6 +52,7 @@ import android.app.ActivityThread; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.BroadcastOptions; +import android.app.ForegroundServiceStartNotAllowedException; import android.app.IApplicationThread; import android.app.IServiceConnection; import android.app.Notification; @@ -693,7 +694,7 @@ public final class ActiveServices { + "could not resolve client package " + callingPackage); } if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, aInfo.uid)) { - throw new IllegalStateException(msg); + throw new ForegroundServiceStartNotAllowedException(msg); } return null; } @@ -1778,7 +1779,7 @@ public final class ActiveServices { ignoreForeground = true; if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, r.appInfo.uid)) { - throw new IllegalStateException(msg); + throw new ForegroundServiceStartNotAllowedException(msg); } } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 5ee0e040019c..33c0f9d628dc 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -142,6 +142,8 @@ import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityClient; import android.app.ActivityManager; +import android.app.ActivityManager.PendingIntentInfo; +import android.app.ActivityManager.ProcessCapability; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManagerInternal; import android.app.ActivityTaskManager.RootTaskInfo; @@ -173,6 +175,7 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.app.ProcessMemoryState; import android.app.ProfilerInfo; +import android.app.PropertyInvalidatedCache; import android.app.WaitResult; import android.app.backup.BackupManager.OperationType; import android.app.backup.IBackupManager; @@ -181,7 +184,6 @@ import android.app.usage.UsageEvents.Event; import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal; import android.appwidget.AppWidgetManager; -import android.compat.Compatibility; import android.content.AutofillOptions; import android.content.BroadcastReceiver; import android.content.ComponentCallbacks2; @@ -306,7 +308,6 @@ import com.android.internal.app.IAppOpsService; import com.android.internal.app.ProcessMap; import com.android.internal.app.SystemUserHomeActivity; import com.android.internal.app.procstats.ProcessStats; -import com.android.internal.compat.CompatibilityChangeConfig; import com.android.internal.content.PackageHelper; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; @@ -4859,19 +4860,6 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public String getPackageForIntentSender(IIntentSender pendingResult) { - if (!(pendingResult instanceof PendingIntentRecord)) { - return null; - } - try { - PendingIntentRecord res = (PendingIntentRecord)pendingResult; - return res.key.packageName; - } catch (ClassCastException e) { - } - return null; - } - - @Override public void registerIntentSenderCancelListener(IIntentSender sender, IResultReceiver receiver) { mPendingIntentController.registerIntentSenderCancelListener(sender, receiver); } @@ -4883,15 +4871,17 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public int getUidForIntentSender(IIntentSender sender) { + public PendingIntentInfo getInfoForIntentSender(IIntentSender sender) { if (sender instanceof PendingIntentRecord) { - try { - PendingIntentRecord res = (PendingIntentRecord)sender; - return res.uid; - } catch (ClassCastException e) { - } + PendingIntentRecord res = (PendingIntentRecord) sender; + return new PendingIntentInfo( + res.key.packageName, + res.uid, + (res.key.flags & PendingIntent.FLAG_IMMUTABLE) != 0, + res.key.type); + } else { + throw new IllegalArgumentException(); } - return -1; } @Override @@ -4917,15 +4907,6 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public boolean isIntentSenderImmutable(IIntentSender pendingResult) { - if (pendingResult instanceof PendingIntentRecord) { - final PendingIntentRecord res = (PendingIntentRecord) pendingResult; - return (res.key.flags & PendingIntent.FLAG_IMMUTABLE) != 0; - } - return false; - } - - @Override public boolean isIntentSenderAnActivity(IIntentSender pendingResult) { if (!(pendingResult instanceof PendingIntentRecord)) { return false; @@ -4942,33 +4923,6 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public boolean isIntentSenderAForegroundService(IIntentSender pendingResult) { - if (pendingResult instanceof PendingIntentRecord) { - final PendingIntentRecord res = (PendingIntentRecord) pendingResult; - return res.key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE; - } - return false; - } - - @Override - public boolean isIntentSenderAService(IIntentSender pendingResult) { - if (pendingResult instanceof PendingIntentRecord) { - final PendingIntentRecord res = (PendingIntentRecord) pendingResult; - return res.key.type == ActivityManager.INTENT_SENDER_SERVICE; - } - return false; - } - - @Override - public boolean isIntentSenderABroadcast(IIntentSender pendingResult) { - if (pendingResult instanceof PendingIntentRecord) { - final PendingIntentRecord res = (PendingIntentRecord) pendingResult; - return res.key.type == ActivityManager.INTENT_SENDER_BROADCAST; - } - return false; - } - - @Override public Intent getIntentForIntentSender(IIntentSender pendingResult) { enforceCallingPermission(Manifest.permission.GET_INTENT_SENDER_INTENT, "getIntentForIntentSender()"); @@ -6083,10 +6037,18 @@ public class ActivityManagerService extends IActivityManager.Stub abiOverride, zygotePolicyFlags); } - // TODO: Move to ProcessList? @GuardedBy("this") final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated, boolean disableHiddenApiChecks, String abiOverride, int zygotePolicyFlags) { + return addAppLocked(info, customProcess, isolated, disableHiddenApiChecks, + false /* disableTestApiChecks */, abiOverride, zygotePolicyFlags); + } + + // TODO: Move to ProcessList? + @GuardedBy("this") + final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated, + boolean disableHiddenApiChecks, boolean disableTestApiChecks, + String abiOverride, int zygotePolicyFlags) { ProcessRecord app; if (!isolated) { app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName, @@ -6121,7 +6083,8 @@ public class ActivityManagerService extends IActivityManager.Stub mPersistentStartingProcesses.add(app); mProcessList.startProcessLocked(app, new HostingRecord("added application", customProcess != null ? customProcess : app.processName), - zygotePolicyFlags, disableHiddenApiChecks, abiOverride); + zygotePolicyFlags, disableHiddenApiChecks, disableTestApiChecks, + abiOverride); } return app; @@ -6624,6 +6587,18 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override + public @ProcessCapability int getUidProcessCapabilities(int uid, String callingPackage) { + if (!hasUsageStatsPermission(callingPackage)) { + enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS, + "getUidProcessState"); + } + + synchronized (mProcLock) { + return mProcessList.getUidProcessCapabilityLOSP(uid); + } + } + + @Override public void registerUidObserver(IUidObserver observer, int which, int cutpoint, String callingPackage) { if (!hasUsageStatsPermission(callingPackage)) { @@ -9863,6 +9838,10 @@ public class ActivityManagerService extends IActivityManager.Stub if (thread != null) { pw.println("\n\n** Cache info for pid " + pid + " [" + r.processName + "] **"); pw.flush(); + if (pid == MY_PID) { + PropertyInvalidatedCache.dumpCacheInfo(fd, args); + continue; + } try { TransferPipe tp = new TransferPipe(); try { @@ -10065,6 +10044,7 @@ public class ActivityManagerService extends IActivityManager.Stub ProcessList.PERSISTENT_SERVICE_ADJ, ProcessList.FOREGROUND_APP_ADJ, ProcessList.VISIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_LOW_APP_ADJ, + ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ, ProcessList.BACKUP_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ, ProcessList.SERVICE_ADJ, ProcessList.HOME_APP_ADJ, ProcessList.PREVIOUS_APP_ADJ, ProcessList.SERVICE_B_ADJ, ProcessList.CACHED_APP_MIN_ADJ @@ -10072,7 +10052,7 @@ public class ActivityManagerService extends IActivityManager.Stub static final String[] DUMP_MEM_OOM_LABEL = new String[] { "Native", "System", "Persistent", "Persistent Service", "Foreground", - "Visible", "Perceptible", "Perceptible Low", + "Visible", "Perceptible", "Perceptible Low", "Perceptible Medium", "Heavy Weight", "Backup", "A Services", "Home", "Previous", "B Services", "Cached" @@ -10080,7 +10060,7 @@ public class ActivityManagerService extends IActivityManager.Stub static final String[] DUMP_MEM_OOM_COMPACT_LABEL = new String[] { "native", "sys", "pers", "persvc", "fore", - "vis", "percept", "perceptl", + "vis", "percept", "perceptl", "perceptm", "heavy", "backup", "servicea", "home", "prev", "serviceb", "cached" @@ -13557,8 +13537,6 @@ public class ActivityManagerService extends IActivityManager.Stub if (disableHiddenApiChecks || disableTestApiChecks) { enforceCallingPermission(android.Manifest.permission.DISABLE_HIDDEN_API_CHECKS, "disable hidden API checks"); - - enableTestApiAccess(ai.packageName); } final long origId = Binder.clearCallingIdentity(); @@ -13576,8 +13554,8 @@ public class ActivityManagerService extends IActivityManager.Stub mUsageStatsService.reportEvent(ii.targetPackage, userId, UsageEvents.Event.SYSTEM_INTERACTION); } - app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks, abiOverride, - ZYGOTE_POLICY_FLAG_EMPTY); + app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks, + disableTestApiChecks, abiOverride, ZYGOTE_POLICY_FLAG_EMPTY); } app.setActiveInstrumentation(activeInstr); @@ -13732,25 +13710,6 @@ public class ActivityManagerService extends IActivityManager.Stub app.userId, "finished inst"); } - - disableTestApiAccess(app.info.packageName); - } - - private void enableTestApiAccess(String packageName) { - if (mPlatformCompat != null) { - Compatibility.ChangeConfig config = new Compatibility.ChangeConfig( - Collections.singleton(166236554L /* VMRuntime.ALLOW_TEST_API_ACCESS */), - Collections.emptySet()); - CompatibilityChangeConfig override = new CompatibilityChangeConfig(config); - mPlatformCompat.setOverridesForTest(override, packageName); - } - } - - private void disableTestApiAccess(String packageName) { - if (mPlatformCompat != null) { - mPlatformCompat.clearOverrideForTest(166236554L /* VMRuntime.ALLOW_TEST_API_ACCESS */, - packageName); - } } public void finishInstrumentation(IApplicationThread target, @@ -13959,7 +13918,7 @@ public class ActivityManagerService extends IActivityManager.Stub } void noteUidProcessState(final int uid, final int state, - final @ActivityManager.ProcessCapability int capability) { + final @ProcessCapability int capability) { mBatteryStatsService.noteUidProcessState(uid, state); mAppOpsService.updateUidProcState(uid, state, capability); if (mTrackingAssociations) { diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index 7bdf43c9c744..a34163cd5fa2 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -66,6 +66,10 @@ public final class CachedAppOptimizer { @VisibleForTesting static final String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4"; @VisibleForTesting static final String KEY_COMPACT_THROTTLE_5 = "compact_throttle_5"; @VisibleForTesting static final String KEY_COMPACT_THROTTLE_6 = "compact_throttle_6"; + @VisibleForTesting static final String KEY_COMPACT_THROTTLE_MIN_OOM_ADJ = + "compact_throttle_min_oom_adj"; + @VisibleForTesting static final String KEY_COMPACT_THROTTLE_MAX_OOM_ADJ = + "compact_throttle_max_oom_adj"; @VisibleForTesting static final String KEY_COMPACT_STATSD_SAMPLE_RATE = "compact_statsd_sample_rate"; @VisibleForTesting static final String KEY_FREEZER_STATSD_SAMPLE_RATE = @@ -101,6 +105,10 @@ public final class CachedAppOptimizer { @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_4 = 10_000; @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_5 = 10 * 60 * 1000; @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_6 = 10 * 60 * 1000; + @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ = + ProcessList.CACHED_APP_MIN_ADJ; + @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ = + ProcessList.CACHED_APP_MAX_ADJ; // The sampling rate to push app compaction events into statsd for upload. @VisibleForTesting static final float DEFAULT_STATSD_SAMPLE_RATE = 0.1f; @VisibleForTesting static final long DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB = 12_000L; @@ -186,6 +194,10 @@ public final class CachedAppOptimizer { updateFullDeltaRssThrottle(); } else if (KEY_COMPACT_PROC_STATE_THROTTLE.equals(name)) { updateProcStateThrottle(); + } else if (KEY_COMPACT_THROTTLE_MIN_OOM_ADJ.equals(name)) { + updateMinOomAdjThrottle(); + } else if (KEY_COMPACT_THROTTLE_MAX_OOM_ADJ.equals(name)) { + updateMaxOomAdjThrottle(); } } } @@ -217,6 +229,12 @@ public final class CachedAppOptimizer { @GuardedBy("mPhenotypeFlagLock") @VisibleForTesting volatile long mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6; @GuardedBy("mPhenotypeFlagLock") + @VisibleForTesting volatile long mCompactThrottleMinOomAdj = + DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ; + @GuardedBy("mPhenotypeFlagLock") + @VisibleForTesting volatile long mCompactThrottleMaxOomAdj = + DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ; + @GuardedBy("mPhenotypeFlagLock") private volatile boolean mUseCompaction = DEFAULT_USE_COMPACTION; private volatile boolean mUseFreezer = DEFAULT_USE_FREEZER; @GuardedBy("this") @@ -282,6 +300,7 @@ public final class CachedAppOptimizer { * starts the background thread if necessary. */ public void init() { + // TODO: initialize flags to default and only update them if values are set in DeviceConfig DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, ActivityThread.currentApplication().getMainExecutor(), mOnFlagsChangedListener); synchronized (mPhenotypeFlagLock) { @@ -294,6 +313,8 @@ public final class CachedAppOptimizer { updateFullDeltaRssThrottle(); updateProcStateThrottle(); updateUseFreezer(); + updateMinOomAdjThrottle(); + updateMaxOomAdjThrottle(); } } @@ -328,6 +349,8 @@ public final class CachedAppOptimizer { pw.println(" " + KEY_COMPACT_THROTTLE_4 + "=" + mCompactThrottleFullFull); pw.println(" " + KEY_COMPACT_THROTTLE_5 + "=" + mCompactThrottleBFGS); pw.println(" " + KEY_COMPACT_THROTTLE_6 + "=" + mCompactThrottlePersistent); + pw.println(" " + KEY_COMPACT_THROTTLE_MIN_OOM_ADJ + "=" + mCompactThrottleMinOomAdj); + pw.println(" " + KEY_COMPACT_THROTTLE_MAX_OOM_ADJ + "=" + mCompactThrottleMaxOomAdj); pw.println(" " + KEY_COMPACT_STATSD_SAMPLE_RATE + "=" + mCompactStatsdSampleRate); pw.println(" " + KEY_COMPACT_FULL_RSS_THROTTLE_KB + "=" + mFullAnonRssThrottleKb); @@ -367,12 +390,23 @@ public final class CachedAppOptimizer { @GuardedBy("mProcLock") void compactAppFull(ProcessRecord app) { - app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_FULL); - mPendingCompactionProcesses.add(app); - mCompactionHandler.sendMessage( + // Apply OOM adj score throttle for Full App Compaction. + if ((app.mState.getSetAdj() < mCompactThrottleMinOomAdj + || app.mState.getSetAdj() > mCompactThrottleMaxOomAdj) + && app.mState.getCurAdj() >= mCompactThrottleMinOomAdj + && app.mState.getCurAdj() <= mCompactThrottleMaxOomAdj) { + app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_FULL); + mPendingCompactionProcesses.add(app); + mCompactionHandler.sendMessage( mCompactionHandler.obtainMessage( - COMPACT_PROCESS_MSG, app.mState.getSetAdj(), app.mState.getSetProcState())); - + COMPACT_PROCESS_MSG, app.mState.getSetAdj(), app.mState.getSetProcState())); + } else { + if (DEBUG_COMPACTION) { + Slog.d(TAG_AM, "Skipping full compaction for " + app.processName + + " oom adj score changed from " + app.mState.getSetAdj() + + " to " + app.mState.getCurAdj()); + } + } } @GuardedBy("mProcLock") @@ -629,6 +663,7 @@ public final class CachedAppOptimizer { @GuardedBy("mPhenotypeFlagLock") private void updateCompactionThrottles() { boolean useThrottleDefaults = false; + // TODO: improve efficiency by calling DeviceConfig only once for all flags. String throttleSomeSomeFlag = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_THROTTLE_1); @@ -647,12 +682,20 @@ public final class CachedAppOptimizer { String throttlePersistentFlag = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_THROTTLE_6); + String throttleMinOomAdjFlag = + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_COMPACT_THROTTLE_MIN_OOM_ADJ); + String throttleMaxOomAdjFlag = + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_COMPACT_THROTTLE_MAX_OOM_ADJ); if (TextUtils.isEmpty(throttleSomeSomeFlag) || TextUtils.isEmpty(throttleSomeFullFlag) || TextUtils.isEmpty(throttleFullSomeFlag) || TextUtils.isEmpty(throttleFullFullFlag) || TextUtils.isEmpty(throttleBFGSFlag) - || TextUtils.isEmpty(throttlePersistentFlag)) { + || TextUtils.isEmpty(throttlePersistentFlag) + || TextUtils.isEmpty(throttleMinOomAdjFlag) + || TextUtils.isEmpty(throttleMaxOomAdjFlag)) { // Set defaults for all if any are not set. useThrottleDefaults = true; } else { @@ -663,6 +706,8 @@ public final class CachedAppOptimizer { mCompactThrottleFullFull = Integer.parseInt(throttleFullFullFlag); mCompactThrottleBFGS = Integer.parseInt(throttleBFGSFlag); mCompactThrottlePersistent = Integer.parseInt(throttlePersistentFlag); + mCompactThrottleMinOomAdj = Long.parseLong(throttleMinOomAdjFlag); + mCompactThrottleMaxOomAdj = Long.parseLong(throttleMaxOomAdjFlag); } catch (NumberFormatException e) { useThrottleDefaults = true; } @@ -675,6 +720,8 @@ public final class CachedAppOptimizer { mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4; mCompactThrottleBFGS = DEFAULT_COMPACT_THROTTLE_5; mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6; + mCompactThrottleMinOomAdj = DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ; + mCompactThrottleMaxOomAdj = DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ; } } @@ -729,6 +776,28 @@ public final class CachedAppOptimizer { } } + @GuardedBy("mPhenotypeFlagLock") + private void updateMinOomAdjThrottle() { + mCompactThrottleMinOomAdj = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_COMPACT_THROTTLE_MIN_OOM_ADJ, DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ); + + // Should only compact cached processes. + if (mCompactThrottleMinOomAdj < ProcessList.CACHED_APP_MIN_ADJ) { + mCompactThrottleMinOomAdj = DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ; + } + } + + @GuardedBy("mPhenotypeFlagLock") + private void updateMaxOomAdjThrottle() { + mCompactThrottleMaxOomAdj = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_COMPACT_THROTTLE_MAX_OOM_ADJ, DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ); + + // Should only compact cached processes. + if (mCompactThrottleMaxOomAdj > ProcessList.CACHED_APP_MAX_ADJ) { + mCompactThrottleMaxOomAdj = DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ; + } + } + private boolean parseProcStateThrottle(String procStateThrottleString) { String[] procStates = TextUtils.split(procStateThrottleString, ","); mProcStateThrottle.clear(); diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 0a8016cf0556..b956e306dc3f 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -21,6 +21,7 @@ import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL_IMPLICIT; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE; +import static android.app.ActivityManager.PROCESS_CAPABILITY_NETWORK; import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE; import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP; @@ -1981,6 +1982,21 @@ public final class OomAdjuster { capability |= cstate.getCurCapability(); } + // If an app has network capability by default + // (by having procstate <= BFGS), then the apps it binds to will get + // elevated to a high enough procstate anyway to get network unless they + // request otherwise, so don't propagate the network capability by default + // in this case unless they explicitly request it. + if ((cstate.getCurCapability() & PROCESS_CAPABILITY_NETWORK) != 0) { + if (clientProcState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { + if ((cr.flags & Context.BIND_ALLOW_NETWORK_ACCESS) != 0) { + capability |= PROCESS_CAPABILITY_NETWORK; + } + } else { + capability |= PROCESS_CAPABILITY_NETWORK; + } + } + if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) { // If the other app is cached for any reason, for purposes here // we are going to consider it empty. The specific cached state @@ -2048,6 +2064,10 @@ public final class OomAdjuster { && clientAdj <= ProcessList.PERCEPTIBLE_APP_ADJ && adj >= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) { newAdj = ProcessList.PERCEPTIBLE_LOW_APP_ADJ; + } else if ((cr.flags & Context.BIND_ALMOST_PERCEPTIBLE) != 0 + && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ + && adj >= ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ) { + newAdj = ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ; } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0 && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ && adj >= ProcessList.PERCEPTIBLE_APP_ADJ) { @@ -2117,13 +2137,13 @@ public final class OomAdjuster { if (enabled) { if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) { // TOP process passes all capabilities to the service. - capability |= PROCESS_CAPABILITY_ALL; + capability |= cstate.getCurCapability(); } else { // TOP process passes no capability to the service. } } else { // TOP process passes all capabilities to the service. - capability |= PROCESS_CAPABILITY_ALL; + capability |= cstate.getCurCapability(); } } } else if ((cr.flags & Context.BIND_IMPORTANT_BACKGROUND) == 0) { @@ -2448,20 +2468,20 @@ public final class OomAdjuster { case PROCESS_STATE_TOP: return PROCESS_CAPABILITY_ALL; case PROCESS_STATE_BOUND_TOP: - return PROCESS_CAPABILITY_NONE; + return PROCESS_CAPABILITY_NETWORK; case PROCESS_STATE_FOREGROUND_SERVICE: if (psr.hasForegroundServices()) { // Capability from FGS are conditional depending on foreground service type in // manifest file and the mAllowWhileInUsePermissionInFgs flag. - return PROCESS_CAPABILITY_NONE; + return PROCESS_CAPABILITY_NETWORK; } else { // process has no FGS, the PROCESS_STATE_FOREGROUND_SERVICE is from client. // the implicit capability could be removed in the future, client should use // BIND_INCLUDE_CAPABILITY flag. - return PROCESS_CAPABILITY_ALL_IMPLICIT; + return PROCESS_CAPABILITY_ALL_IMPLICIT | PROCESS_CAPABILITY_NETWORK; } case PROCESS_STATE_BOUND_FOREGROUND_SERVICE: - return PROCESS_CAPABILITY_NONE; + return PROCESS_CAPABILITY_NETWORK; default: return PROCESS_CAPABILITY_NONE; } @@ -2541,9 +2561,7 @@ public final class OomAdjuster { && (state.getCurAdj() == ProcessList.PREVIOUS_APP_ADJ || state.getCurAdj() == ProcessList.HOME_APP_ADJ)) { mCachedAppOptimizer.compactAppSome(app); - } else if ((state.getSetAdj() < ProcessList.CACHED_APP_MIN_ADJ - || state.getSetAdj() > ProcessList.CACHED_APP_MAX_ADJ) - && state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ + } else if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ && state.getCurAdj() <= ProcessList.CACHED_APP_MAX_ADJ) { mCachedAppOptimizer.compactAppFull(app); } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 172f7073852a..38330fe770fb 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -16,6 +16,7 @@ package com.android.server.am; +import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE; import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY; import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; import static android.app.ActivityThread.PROC_START_SEQ_IDENT; @@ -57,6 +58,7 @@ import static com.android.server.am.AppProfiler.TAG_PSS; import android.annotation.NonNull; import android.app.ActivityManager; +import android.app.ActivityManager.ProcessCapability; import android.app.ActivityThread; import android.app.AppGlobals; import android.app.AppProtoEnums; @@ -228,6 +230,11 @@ public final class ProcessList { // not so perceptible that it affects the user immediately if killed. static final int PERCEPTIBLE_LOW_APP_ADJ = 250; + // This is a process hosting services that are not perceptible to the user but the + // client (system) binding to it requested to treat it as if it is perceptible and avoid killing + // it if possible. + static final int PERCEPTIBLE_MEDIUM_APP_ADJ = 225; + // This is a process only hosting components that are perceptible to the // user, and we really want to avoid killing them, but they are not // immediately visible. An example is background music playback. @@ -1027,6 +1034,9 @@ public final class ProcessList { } else if (setAdj >= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) { return buildOomTag("prcl ", "prcl", null, setAdj, ProcessList.PERCEPTIBLE_LOW_APP_ADJ, compact); + } else if (setAdj >= ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ) { + return buildOomTag("prcm ", "prcm", null, setAdj, + ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ, compact); } else if (setAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) { return buildOomTag("prcp ", "prcp", null, setAdj, ProcessList.PERCEPTIBLE_APP_ADJ, compact); @@ -1054,76 +1064,7 @@ public final class ProcessList { } public static String makeProcStateString(int curProcState) { - String procState; - switch (curProcState) { - case ActivityManager.PROCESS_STATE_PERSISTENT: - procState = "PER "; - break; - case ActivityManager.PROCESS_STATE_PERSISTENT_UI: - procState = "PERU"; - break; - case ActivityManager.PROCESS_STATE_TOP: - procState = "TOP "; - break; - case ActivityManager.PROCESS_STATE_BOUND_TOP: - procState = "BTOP"; - break; - case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE: - procState = "FGS "; - break; - case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE: - procState = "BFGS"; - break; - case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND: - procState = "IMPF"; - break; - case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND: - procState = "IMPB"; - break; - case ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND: - procState = "TRNB"; - break; - case ActivityManager.PROCESS_STATE_BACKUP: - procState = "BKUP"; - break; - case ActivityManager.PROCESS_STATE_SERVICE: - procState = "SVC "; - break; - case ActivityManager.PROCESS_STATE_RECEIVER: - procState = "RCVR"; - break; - case ActivityManager.PROCESS_STATE_TOP_SLEEPING: - procState = "TPSL"; - break; - case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT: - procState = "HVY "; - break; - case ActivityManager.PROCESS_STATE_HOME: - procState = "HOME"; - break; - case ActivityManager.PROCESS_STATE_LAST_ACTIVITY: - procState = "LAST"; - break; - case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: - procState = "CAC "; - break; - case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: - procState = "CACC"; - break; - case ActivityManager.PROCESS_STATE_CACHED_RECENT: - procState = "CRE "; - break; - case ActivityManager.PROCESS_STATE_CACHED_EMPTY: - procState = "CEM "; - break; - case ActivityManager.PROCESS_STATE_NONEXISTENT: - procState = "NONE"; - break; - default: - procState = "??"; - break; - } - return procState; + return ActivityManager.procStateToString(curProcState); } public static int makeProcStateProtoEnum(int curProcState) { @@ -1828,7 +1769,8 @@ public final class ProcessList { */ @GuardedBy("mService") boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord, - int zygotePolicyFlags, boolean disableHiddenApiChecks, String abiOverride) { + int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean disableTestApiChecks, + String abiOverride) { if (app.isPendingStart()) { return true; } @@ -1974,6 +1916,10 @@ public final class ProcessList { throw new IllegalStateException("Invalid API policy: " + policy); } runtimeFlags |= policyBits; + + if (disableTestApiChecks) { + runtimeFlags |= Zygote.DISABLE_TEST_API_ENFORCEMENT_POLICY; + } } String useAppImageCache = SystemProperties.get( @@ -2437,7 +2383,8 @@ public final class ProcessList { boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord, int zygotePolicyFlags, String abiOverride) { return startProcessLocked(app, hostingRecord, zygotePolicyFlags, - false /* disableHiddenApiChecks */, abiOverride); + false /* disableHiddenApiChecks */, false /* disableTestApiChecks */, + abiOverride); } @GuardedBy("mService") @@ -4458,6 +4405,7 @@ public final class ProcessList { printOomLevel(pw, "FOREGROUND_APP_ADJ", FOREGROUND_APP_ADJ); printOomLevel(pw, "VISIBLE_APP_ADJ", VISIBLE_APP_ADJ); printOomLevel(pw, "PERCEPTIBLE_APP_ADJ", PERCEPTIBLE_APP_ADJ); + printOomLevel(pw, "PERCEPTIBLE_MEDIUM_APP_ADJ", PERCEPTIBLE_MEDIUM_APP_ADJ); printOomLevel(pw, "PERCEPTIBLE_LOW_APP_ADJ", PERCEPTIBLE_LOW_APP_ADJ); printOomLevel(pw, "BACKUP_APP_ADJ", BACKUP_APP_ADJ); printOomLevel(pw, "HEAVY_WEIGHT_APP_ADJ", HEAVY_WEIGHT_APP_ADJ); @@ -4721,13 +4669,26 @@ public final class ProcessList { } } - /** Returns the uid's process state or PROCESS_STATE_NONEXISTENT if not running */ + /** + * Returns the uid's process state or {@link ActivityManager#PROCESS_STATE_NONEXISTENT} + * if not running + */ @GuardedBy(anyOf = {"mService", "mProcLock"}) int getUidProcStateLOSP(int uid) { UidRecord uidRec = mActiveUids.get(uid); return uidRec == null ? PROCESS_STATE_NONEXISTENT : uidRec.getCurProcState(); } + /** + * Returns the uid's process capability or {@link ActivityManager#PROCESS_CAPABILITY_NONE} + * if not running + */ + @GuardedBy(anyOf = {"mService", "mProcLock"}) + @ProcessCapability int getUidProcessCapabilityLOSP(int uid) { + UidRecord uidRec = mActiveUids.get(uid); + return uidRec == null ? PROCESS_CAPABILITY_NONE : uidRec.getCurCapability(); + } + /** Returns the UidRecord for the given uid, if it exists. */ @GuardedBy(anyOf = {"mService", "mProcLock"}) UidRecord getUidRecordLOSP(int uid) { @@ -4768,11 +4729,13 @@ public final class ProcessList { int getBlockStateForUid(UidRecord uidRec) { // Denotes whether uid's process state is currently allowed network access. final boolean isAllowed = - isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getCurProcState()) + isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getCurProcState(), + uidRec.getCurCapability()) || isProcStateAllowedWhileOnRestrictBackground(uidRec.getCurProcState()); // Denotes whether uid's process state was previously allowed network access. final boolean wasAllowed = - isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getSetProcState()) + isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getSetProcState(), + uidRec.getSetCapability()) || isProcStateAllowedWhileOnRestrictBackground(uidRec.getSetProcState()); // When the uid is coming to foreground, AMS should inform the app thread that it should @@ -4810,8 +4773,9 @@ public final class ProcessList { if (!UserHandle.isApp(uidRec.getUid()) || !uidRec.hasInternetPermission) { continue; } - // If process state is not changed, then there's nothing to do. - if (uidRec.getSetProcState() == uidRec.getCurProcState()) { + // If process state and capabilities are not changed, then there's nothing to do. + if (uidRec.getSetProcState() == uidRec.getCurProcState() + && uidRec.getSetCapability() == uidRec.getCurCapability()) { continue; } final int blockState = getBlockStateForUid(uidRec); diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java index e97f0b47380a..33bdac270c53 100644 --- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java +++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java @@ -23,9 +23,12 @@ import static android.content.Intent.EXTRA_REPLACING; import static android.content.pm.PackageManager.MATCH_ANY_USER; import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION; +import static com.android.server.apphibernation.AppHibernationConstants.KEY_APP_HIBERNATION_ENABLED; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityThread; import android.app.IActivityManager; import android.apphibernation.IAppHibernationService; import android.content.BroadcastReceiver; @@ -45,6 +48,8 @@ import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.provider.DeviceConfig; +import android.provider.DeviceConfig.Properties; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; @@ -93,6 +98,9 @@ public final class AppHibernationService extends SystemService { private final HibernationStateDiskStore<GlobalLevelState> mGlobalLevelHibernationDiskStore; private final Injector mInjector; + @VisibleForTesting + boolean mIsServiceEnabled; + /** * Initializes the system service. * <p> @@ -139,6 +147,13 @@ public final class AppHibernationService extends SystemService { initializeGlobalHibernationStates(states); } } + if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { + mIsServiceEnabled = isAppHibernationEnabled(); + DeviceConfig.addOnPropertiesChangedListener( + NAMESPACE_APP_HIBERNATION, + ActivityThread.currentApplication().getMainExecutor(), + this::onDeviceConfigChanged); + } } /** @@ -149,6 +164,10 @@ public final class AppHibernationService extends SystemService { * @return true if package is hibernating for the user */ boolean isHibernatingForUser(String packageName, int userId) { + if (!checkHibernationEnabled("isHibernatingForUser")) { + return false; + } + userId = handleIncomingUser(userId, "isHibernating"); if (!mUserManager.isUserUnlockingOrUnlocked(userId)) { Slog.e(TAG, "Attempt to get hibernation state of stopped or nonexistent user " @@ -174,6 +193,9 @@ public final class AppHibernationService extends SystemService { * @param packageName package to check */ boolean isHibernatingGlobally(String packageName) { + if (!checkHibernationEnabled("isHibernatingGlobally")) { + return false; + } synchronized (mLock) { GlobalLevelState state = mGlobalHibernationStates.get(packageName); if (state == null) { @@ -192,6 +214,9 @@ public final class AppHibernationService extends SystemService { * @param isHibernating new hibernation state */ void setHibernatingForUser(String packageName, int userId, boolean isHibernating) { + if (!checkHibernationEnabled("setHibernatingForUser")) { + return; + } userId = handleIncomingUser(userId, "setHibernating"); if (!mUserManager.isUserUnlockingOrUnlocked(userId)) { Slog.w(TAG, "Attempt to set hibernation state for a stopped or nonexistent user " @@ -229,6 +254,9 @@ public final class AppHibernationService extends SystemService { * @param isHibernating new hibernation state */ void setHibernatingGlobally(String packageName, boolean isHibernating) { + if (!checkHibernationEnabled("setHibernatingGlobally")) { + return; + } synchronized (mLock) { GlobalLevelState state = mGlobalHibernationStates.get(packageName); if (state == null) { @@ -421,6 +449,9 @@ public final class AppHibernationService extends SystemService { private void onPackageAdded(@NonNull String packageName, int userId) { synchronized (mLock) { + if (!mUserStates.contains(userId)) { + return; + } UserLevelState userState = new UserLevelState(); userState.packageName = packageName; mUserStates.get(userId).put(packageName, userState); @@ -434,6 +465,9 @@ public final class AppHibernationService extends SystemService { private void onPackageRemoved(@NonNull String packageName, int userId) { synchronized (mLock) { + if (!mUserStates.contains(userId)) { + return; + } mUserStates.get(userId).remove(packageName); } } @@ -444,6 +478,15 @@ public final class AppHibernationService extends SystemService { } } + private void onDeviceConfigChanged(Properties properties) { + for (String key : properties.getKeyset()) { + if (TextUtils.equals(KEY_APP_HIBERNATION_ENABLED, key)) { + mIsServiceEnabled = isAppHibernationEnabled(); + break; + } + } + } + /** * Private helper method to get the real user id and enforce permission checks. * @@ -461,6 +504,13 @@ public final class AppHibernationService extends SystemService { } } + private boolean checkHibernationEnabled(String methodName) { + if (!mIsServiceEnabled) { + Slog.w(TAG, String.format("Attempted to call %s on unsupported device.", methodName)); + } + return mIsServiceEnabled; + } + private final AppHibernationServiceStub mServiceStub = new AppHibernationServiceStub(this); static final class AppHibernationServiceStub extends IAppHibernationService.Stub { @@ -536,7 +586,7 @@ public final class AppHibernationService extends SystemService { public static boolean isAppHibernationEnabled() { return DeviceConfig.getBoolean( NAMESPACE_APP_HIBERNATION, - AppHibernationConstants.KEY_APP_HIBERNATION_ENABLED, + KEY_APP_HIBERNATION_ENABLED, false /* defaultValue */); } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 5f3405379715..f5b94177a2d9 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -8570,6 +8570,7 @@ public class AudioService extends IAudioService.Stub public void postDisplaySafeVolumeWarning(int flags) { if (mController == null) return; + flags = flags | AudioManager.FLAG_SHOW_UI; try { mController.displaySafeVolumeWarning(flags); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ReEnrollNotificationUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/ReEnrollNotificationUtils.java new file mode 100644 index 000000000000..f35a5208f6ed --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/ReEnrollNotificationUtils.java @@ -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. + */ + +package com.android.server.biometrics.sensors.face; + +import android.annotation.NonNull; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.UserHandle; + +import com.android.internal.R; + +public class ReEnrollNotificationUtils { + + private static final String NOTIFICATION_TAG = "FaceService"; + private static final int NOTIFICATION_ID = 1; + + public static void showReEnrollmentNotification(@NonNull Context context) { + final NotificationManager notificationManager = + context.getSystemService(NotificationManager.class); + + final String name = + context.getString(R.string.face_recalibrate_notification_name); + final String title = + context.getString(R.string.face_recalibrate_notification_title); + final String content = + context.getString(R.string.face_recalibrate_notification_content); + + final Intent intent = new Intent("android.settings.FACE_SETTINGS"); + intent.setPackage("com.android.settings"); + + final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, + 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */, + null /* options */, UserHandle.CURRENT); + + final String channelName = "FaceEnrollNotificationChannel"; + + final NotificationChannel channel = new NotificationChannel(channelName, name, + NotificationManager.IMPORTANCE_HIGH); + final Notification notification = new Notification.Builder(context, channelName) + .setSmallIcon(R.drawable.ic_lock) + .setContentTitle(title) + .setContentText(content) + .setSubText(name) + .setOnlyAlertOnce(true) + .setLocalOnly(true) + .setAutoCancel(true) + .setCategory(Notification.CATEGORY_SYSTEM) + .setContentIntent(pendingIntent) + .setVisibility(Notification.VISIBILITY_SECRET) + .build(); + + notificationManager.createNotificationChannel(channel); + notificationManager.notifyAsUser(NOTIFICATION_TAG, + NOTIFICATION_ID, notification, + UserHandle.CURRENT); + } + + public static void cancelNotification(@NonNull Context context) { + final NotificationManager notificationManager = + context.getSystemService(NotificationManager.class); + notificationManager.cancelAsUser(NOTIFICATION_TAG, NOTIFICATION_ID, UserHandle.CURRENT); + } + +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java index 8f554028ebfd..089cf1e4cee8 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java @@ -41,6 +41,7 @@ import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.LockoutCache; import com.android.server.biometrics.sensors.LockoutConsumer; import com.android.server.biometrics.sensors.LockoutTracker; +import com.android.server.biometrics.sensors.face.ReEnrollNotificationUtils; import com.android.server.biometrics.sensors.face.UsageStats; import java.util.ArrayList; @@ -163,6 +164,9 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements vibrateError(); } break; + case BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL: + ReEnrollNotificationUtils.showReEnrollmentNotification(getContext()); + break; default: break; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java index 898d81b0c8c4..0eb51fdba159 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java @@ -41,6 +41,7 @@ import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.EnrollClient; import com.android.server.biometrics.sensors.face.FaceUtils; +import com.android.server.biometrics.sensors.face.ReEnrollNotificationUtils; import java.io.IOException; import java.util.ArrayList; @@ -85,6 +86,13 @@ public class FaceEnrollClient extends EnrollClient<ISession> { } @Override + public void start(@NonNull Callback callback) { + super.start(callback); + + ReEnrollNotificationUtils.cancelNotification(getContext()); + } + + @Override public void destroy() { try { AidlNativeHandleUtils.close(mPreviewSurface); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index ee8823e041bc..1b9bd7fd0cea 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -19,7 +19,6 @@ package com.android.server.biometrics.sensors.face.hidl; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; -import android.app.NotificationManager; import android.app.SynchronousUserSwitchObserver; import android.app.UserSwitchObserver; import android.content.Context; @@ -71,6 +70,7 @@ import com.android.server.biometrics.sensors.PerformanceTracker; import com.android.server.biometrics.sensors.RemovalConsumer; import com.android.server.biometrics.sensors.face.FaceUtils; import com.android.server.biometrics.sensors.face.LockoutHalImpl; +import com.android.server.biometrics.sensors.face.ReEnrollNotificationUtils; import com.android.server.biometrics.sensors.face.ServiceProvider; import com.android.server.biometrics.sensors.face.UsageStats; @@ -96,8 +96,6 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { private static final String TAG = "Face10"; private static final int ENROLL_TIMEOUT_SEC = 75; - static final String NOTIFICATION_TAG = "FaceService"; - static final int NOTIFICATION_ID = 1; private boolean mTestHalEnabled; @@ -109,7 +107,6 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher; @NonNull private final LockoutHalImpl mLockoutTracker; @NonNull private final UsageStats mUsageStats; - @NonNull private final NotificationManager mNotificationManager; @NonNull private final Map<Integer, Long> mAuthenticatorIds; @Nullable private IBiometricsFace mDaemon; @NonNull private final HalResultController mHalResultController; @@ -343,7 +340,6 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { mUsageStats = new UsageStats(context); mAuthenticatorIds = new HashMap<>(); mLazyDaemon = Face10.this::getDaemon; - mNotificationManager = mContext.getSystemService(NotificationManager.class); mLockoutTracker = new LockoutHalImpl(); mLockoutResetDispatcher = lockoutResetDispatcher; mHalResultController = new HalResultController(sensorId, context, mHandler, mScheduler, @@ -607,8 +603,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); - mNotificationManager.cancelAsUser(NOTIFICATION_TAG, NOTIFICATION_ID, - UserHandle.CURRENT); + ReEnrollNotificationUtils.cancelNotification(mContext); final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java index a4b3ac57a4df..3ca51d32797e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java @@ -17,12 +17,7 @@ package com.android.server.biometrics.sensors.face.hidl; import android.annotation.NonNull; -import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.app.PendingIntent; import android.content.Context; -import android.content.Intent; import android.content.res.Resources; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; @@ -32,7 +27,6 @@ import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.hardware.face.FaceManager; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Slog; import com.android.internal.R; @@ -40,6 +34,7 @@ import com.android.server.biometrics.Utils; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.LockoutTracker; +import com.android.server.biometrics.sensors.face.ReEnrollNotificationUtils; import com.android.server.biometrics.sensors.face.UsageStats; import java.util.ArrayList; @@ -52,7 +47,7 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { private static final String TAG = "FaceAuthenticationClient"; - private final NotificationManager mNotificationManager; + private final UsageStats mUsageStats; private final int[] mBiometricPromptIgnoreList; @@ -72,7 +67,6 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { owner, cookie, requireConfirmation, sensorId, isStrongBiometric, BiometricsProtoEnums.MODALITY_FACE, statsClient, null /* taskStackListener */, lockoutTracker, isKeyguard); - mNotificationManager = context.getSystemService(NotificationManager.class); mUsageStats = usageStats; final Resources resources = getContext().getResources(); @@ -188,41 +182,7 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { mLastAcquire = acquireInfo; if (acquireInfo == FaceManager.FACE_ACQUIRED_RECALIBRATE) { - final String name = - getContext().getString(R.string.face_recalibrate_notification_name); - final String title = - getContext().getString(R.string.face_recalibrate_notification_title); - final String content = - getContext().getString(R.string.face_recalibrate_notification_content); - - final Intent intent = new Intent("android.settings.FACE_SETTINGS"); - intent.setPackage("com.android.settings"); - - final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(getContext(), - 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */, - null /* options */, UserHandle.CURRENT); - - final String channelName = "FaceEnrollNotificationChannel"; - - NotificationChannel channel = new NotificationChannel(channelName, name, - NotificationManager.IMPORTANCE_HIGH); - Notification notification = new Notification.Builder(getContext(), channelName) - .setSmallIcon(R.drawable.ic_lock) - .setContentTitle(title) - .setContentText(content) - .setSubText(name) - .setOnlyAlertOnce(true) - .setLocalOnly(true) - .setAutoCancel(true) - .setCategory(Notification.CATEGORY_SYSTEM) - .setContentIntent(pendingIntent) - .setVisibility(Notification.VISIBILITY_SECRET) - .build(); - - mNotificationManager.createNotificationChannel(channel); - mNotificationManager.notifyAsUser(Face10.NOTIFICATION_TAG, - Face10.NOTIFICATION_ID, notification, - UserHandle.CURRENT); + ReEnrollNotificationUtils.showReEnrollmentNotification(getContext()); } final boolean shouldSend = shouldSend(acquireInfo, vendorCode); diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java index 46c49e7fc28c..641287f0f435 100644 --- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java +++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java @@ -16,6 +16,8 @@ package com.android.server.connectivity; +import static com.android.net.module.util.CollectionUtils.contains; + import android.annotation.NonNull; import android.net.ConnectivityManager; import android.net.IDnsResolver; @@ -33,7 +35,6 @@ import android.os.ServiceSpecificException; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.ArrayUtils; import com.android.net.module.util.NetworkStackConstants; import com.android.server.net.BaseNetworkObserver; @@ -117,8 +118,8 @@ public class Nat464Xlat extends BaseNetworkObserver { @VisibleForTesting protected static boolean requiresClat(NetworkAgentInfo nai) { // TODO: migrate to NetworkCapabilities.TRANSPORT_*. - final boolean supported = ArrayUtils.contains(NETWORK_TYPES, nai.networkInfo.getType()); - final boolean connected = ArrayUtils.contains(NETWORK_STATES, nai.networkInfo.getState()); + final boolean supported = contains(NETWORK_TYPES, nai.networkInfo.getType()); + final boolean connected = contains(NETWORK_STATES, nai.networkInfo.getState()); // Only run clat on networks that have a global IPv6 address and don't have a native IPv4 // address. diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java index 8bf188696c27..9411e33434d8 100644 --- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java +++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java @@ -28,6 +28,8 @@ import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; import static android.os.Process.INVALID_UID; import static android.os.Process.SYSTEM_UID; +import static com.android.net.module.util.CollectionUtils.toIntArray; + import android.annotation.NonNull; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -40,23 +42,21 @@ import android.net.UidRange; import android.os.Build; import android.os.RemoteException; import android.os.ServiceSpecificException; +import android.os.SystemConfigManager; import android.os.UserHandle; import android.os.UserManager; import android.system.OsConstants; -import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; import android.util.SparseIntArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.ArrayUtils; import com.android.internal.util.IndentingPrintWriter; +import com.android.net.module.util.CollectionUtils; import com.android.server.LocalServices; -import com.android.server.SystemConfig; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -80,6 +80,7 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse private final PackageManager mPackageManager; private final UserManager mUserManager; + private final SystemConfigManager mSystemConfigManager; private final INetd mNetd; private final Dependencies mDeps; @@ -123,6 +124,7 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse @NonNull final Dependencies deps) { mPackageManager = context.getPackageManager(); mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); + mSystemConfigManager = context.getSystemService(SystemConfigManager.class); mNetd = netd; mDeps = deps; } @@ -174,20 +176,18 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse mUsers.addAll(mUserManager.getUserHandles(true /* excludeDying */)); - final SparseArray<ArraySet<String>> systemPermission = - SystemConfig.getInstance().getSystemPermissions(); - for (int i = 0; i < systemPermission.size(); i++) { - ArraySet<String> perms = systemPermission.valueAt(i); - int uid = systemPermission.keyAt(i); - int netdPermission = 0; - // Get the uids of native services that have UPDATE_DEVICE_STATS or INTERNET permission. - if (perms != null) { - netdPermission |= perms.contains(UPDATE_DEVICE_STATS) - ? INetd.PERMISSION_UPDATE_DEVICE_STATS : 0; - netdPermission |= perms.contains(INTERNET) - ? INetd.PERMISSION_INTERNET : 0; + final SparseArray<String> netdPermToSystemPerm = new SparseArray<>(); + netdPermToSystemPerm.put(INetd.PERMISSION_INTERNET, INTERNET); + netdPermToSystemPerm.put(INetd.PERMISSION_UPDATE_DEVICE_STATS, UPDATE_DEVICE_STATS); + for (int i = 0; i < netdPermToSystemPerm.size(); i++) { + final int netdPermission = netdPermToSystemPerm.keyAt(i); + final String systemPermission = netdPermToSystemPerm.valueAt(i); + final int[] hasPermissionUids = + mSystemConfigManager.getSystemPermissionUids(systemPermission); + for (int j = 0; j < hasPermissionUids.length; j++) { + final int uid = hasPermissionUids[j]; + netdPermsUids.put(uid, netdPermsUids.get(uid) | netdPermission); } - netdPermsUids.put(uid, netdPermsUids.get(uid) | netdPermission); } log("Users: " + mUsers.size() + ", Apps: " + mApps.size()); update(mUsers, mApps, true); @@ -204,7 +204,7 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse if (app.requestedPermissions == null || app.requestedPermissionsFlags == null) { return false; } - final int index = ArrayUtils.indexOf(app.requestedPermissions, permission); + final int index = CollectionUtils.indexOf(app.requestedPermissions, permission); if (index < 0 || index >= app.requestedPermissionsFlags.length) return false; return (app.requestedPermissionsFlags[index] & REQUESTED_PERMISSION_GRANTED) != 0; } @@ -246,15 +246,6 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse return mApps.containsKey(uid); } - private int[] toIntArray(Collection<Integer> list) { - int[] array = new int[list.size()]; - int i = 0; - for (Integer item : list) { - array[i++] = item; - } - return array; - } - private void update(Set<UserHandle> users, Map<Integer, Boolean> apps, boolean add) { List<Integer> network = new ArrayList<>(); List<Integer> system = new ArrayList<>(); @@ -662,23 +653,23 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse if (allPermissionAppIds.size() != 0) { mNetd.trafficSetNetPermForUids( INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS, - ArrayUtils.convertToIntArray(allPermissionAppIds)); + toIntArray(allPermissionAppIds)); } if (internetPermissionAppIds.size() != 0) { mNetd.trafficSetNetPermForUids(INetd.PERMISSION_INTERNET, - ArrayUtils.convertToIntArray(internetPermissionAppIds)); + toIntArray(internetPermissionAppIds)); } if (updateStatsPermissionAppIds.size() != 0) { mNetd.trafficSetNetPermForUids(INetd.PERMISSION_UPDATE_DEVICE_STATS, - ArrayUtils.convertToIntArray(updateStatsPermissionAppIds)); + toIntArray(updateStatsPermissionAppIds)); } if (noPermissionAppIds.size() != 0) { mNetd.trafficSetNetPermForUids(INetd.PERMISSION_NONE, - ArrayUtils.convertToIntArray(noPermissionAppIds)); + toIntArray(noPermissionAppIds)); } if (uninstalledAppIds.size() != 0) { mNetd.trafficSetNetPermForUids(INetd.PERMISSION_UNINSTALLED, - ArrayUtils.convertToIntArray(uninstalledAppIds)); + toIntArray(uninstalledAppIds)); } } catch (RemoteException e) { Log.e(TAG, "Pass appId list of special permission failed." + e); diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index a769e88f77d7..01ac81fb2cb5 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -113,6 +113,7 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnProfile; +import com.android.net.module.util.NetdUtils; import com.android.net.module.util.NetworkStackConstants; import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; @@ -1509,7 +1510,7 @@ public class Vpn { if (start != -1) ranges.add(new UidRange(start, stop)); } else if (disallowedApplications != null) { // Add all ranges for user skipping UIDs for disallowedApplications. - final UidRange userRange = UidRange.createForUser(userId); + final UidRange userRange = UidRange.createForUser(UserHandle.of(userId)); int start = userRange.start; for (int uid : getAppsUids(disallowedApplications, userId)) { if (uid == start) { @@ -1522,7 +1523,7 @@ public class Vpn { if (start <= userRange.stop) ranges.add(new UidRange(start, userRange.stop)); } else { // Add all UIDs for the user. - ranges.add(UidRange.createForUser(userId)); + ranges.add(UidRange.createForUser(UserHandle.of(userId))); } } @@ -1531,7 +1532,7 @@ public class Vpn { private static List<UidRange> uidRangesForUser(int userId, Set<UidRange> existingRanges) { // UidRange#createForUser returns the entire range of UIDs available to a macro-user. // This is something like 0-99999 ; {@see UserHandle#PER_USER_RANGE} - final UidRange userRange = UidRange.createForUser(userId); + final UidRange userRange = UidRange.createForUser(UserHandle.of(userId)); final List<UidRange> ranges = new ArrayList<>(); for (UidRange range : existingRanges) { if (userRange.containsRange(range)) { @@ -2528,7 +2529,7 @@ public class Vpn { address /* unused */, address /* unused */, network); - mNms.setInterfaceUp(mTunnelIface.getInterfaceName()); + NetdUtils.setInterfaceUp(mNetd, mTunnelIface.getInterfaceName()); mSession = mIkev2SessionCreator.createIkeSession( mContext, diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index fe89903c02d7..7b9ca37b1639 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -264,6 +264,10 @@ public class SyncManager { private final SyncLogger mLogger; + // NOTE: this is a temporary allow-list for testing purposes; it will be removed before release. + private final String[] mEjSyncAllowedPackages = new String[]{ + "com.google.android.google", "com.android.frameworks.servicestests"}; + private boolean isJobIdInUseLockedH(int jobId, List<JobInfo> pendingJobs) { for (JobInfo job: pendingJobs) { if (job.getId() == jobId) { @@ -983,6 +987,14 @@ public class SyncManager { } } + final boolean scheduleAsEj = + extras.getBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, false); + // NOTE: this is a temporary check for internal testing - to be removed before release. + if (scheduleAsEj && !ArrayUtils.contains(mEjSyncAllowedPackages, callingPackage)) { + throw new IllegalArgumentException( + callingPackage + " is not allowed to schedule a sync as an EJ yet."); + } + for (AccountAndUser account : accounts) { // If userId is specified, do not sync accounts of other users if (userId >= UserHandle.USER_SYSTEM && account.userId >= UserHandle.USER_SYSTEM @@ -1490,6 +1502,12 @@ public class SyncManager { + logSafe(syncOperation.target)); backoff = new Pair<Long, Long>(SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE); + } else { + // if an EJ is being backed-off but doesn't have SYNC_EXTRAS_IGNORE_BACKOFF set, + // reschedule it as a regular job + if (syncOperation.isScheduledAsExpeditedJob()) { + syncOperation.scheduleEjAsRegularJob = true; + } } long now = SystemClock.elapsedRealtime(); long backoffDelay = backoff.first == SyncStorageEngine.NOT_IN_BACKOFF_MODE ? 0 @@ -1640,6 +1658,10 @@ public class SyncManager { b.setRequiresCharging(true); } + if (syncOperation.isScheduledAsExpeditedJob() && !syncOperation.scheduleEjAsRegularJob) { + b.setExpedited(true); + } + if (syncOperation.syncExemptionFlag == ContentResolver.SYNC_EXEMPTION_PROMOTE_BUCKET_WITH_TEMP) { DeviceIdleInternal dic = @@ -3951,6 +3973,9 @@ public class SyncManager { if (key.equals(ContentResolver.SYNC_EXTRAS_EXPEDITED)) { return true; } + if (key.equals(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB)) { + return true; + } if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS)) { return true; } diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java index 478763531abc..c8654d1b36ee 100644 --- a/services/core/java/com/android/server/content/SyncOperation.java +++ b/services/core/java/com/android/server/content/SyncOperation.java @@ -103,6 +103,13 @@ public class SyncOperation { /** Stores the number of times this sync operation failed and had to be retried. */ int retries; + /** + * Indicates if a sync that was originally scheduled as an EJ is being re-scheduled as a + * regular job. Specifically, this will be {@code true} if a sync is being backed-off but + * {@link ContentResolver#SYNC_EXTRAS_IGNORE_BACKOFF} is not set. + */ + boolean scheduleEjAsRegularJob; + /** jobId of the JobScheduler job corresponding to this sync */ public int jobId; @@ -408,6 +415,12 @@ public class SyncOperation { if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) { sb.append(" EXPEDITED"); } + if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, false)) { + sb.append(" EXPEDITED-JOB"); + if (scheduleEjAsRegularJob) { + sb.append("(scheduled-as-regular)"); + } + } switch (syncExemptionFlag) { case ContentResolver.SYNC_EXEMPTION_NONE: break; @@ -537,6 +550,11 @@ public class SyncOperation { return mImmutableExtras.getBoolean(ContentResolver.SYNC_EXTRAS_REQUIRE_CHARGING, false); } + boolean isScheduledAsExpeditedJob() { + return mImmutableExtras.getBoolean( + ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, false); + } + boolean isAppStandbyExempted() { return syncExemptionFlag != ContentResolver.SYNC_EXEMPTION_NONE; } diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index 225da7ad87b3..a2b9b966dd46 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -354,6 +354,10 @@ class AutomaticBrightnessController { } } + public void stop() { + setLightSensorEnabled(false); + } + public boolean hasUserDataPoints() { return mBrightnessMapper.hasUserDataPoints(); } diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java index a186e334c5c0..06010f51e231 100644 --- a/services/core/java/com/android/server/display/BrightnessTracker.java +++ b/services/core/java/com/android/server/display/BrightnessTracker.java @@ -243,7 +243,6 @@ public class BrightnessTracker { } /** Stop listening for events */ - @VisibleForTesting void stop() { if (DEBUG) { Slog.d(TAG, "Stop"); diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java index 198ea3d3f66a..f2126987ff19 100644 --- a/services/core/java/com/android/server/display/ColorFade.java +++ b/services/core/java/com/android/server/display/ColorFade.java @@ -817,6 +817,12 @@ final class ColorFade { } DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId); + if (displayInfo == null) { + // displayInfo can be null if the associated display has been removed. There + // is a delay between the display being removed and ColorFade being dismissed. + return; + } + switch (displayInfo.rotation) { case Surface.ROTATION_0: t.setPosition(mSurfaceControl, 0, 0); diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index cd17cfef2726..b3070b7cf1ba 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -16,6 +16,7 @@ package com.android.server.display; +import android.content.Context; import android.graphics.Rect; import android.hardware.display.DisplayViewport; import android.os.IBinder; @@ -38,12 +39,14 @@ abstract class DisplayDevice { private final IBinder mDisplayToken; private final String mUniqueId; + protected DisplayDeviceConfig mDisplayDeviceConfig; // The display device does not manage these properties itself, they are set by // the display manager service. The display device shouldn't really be looking at these. private int mCurrentLayerStack = -1; private int mCurrentOrientation = -1; private Rect mCurrentLayerStackRect; private Rect mCurrentDisplayRect; + private final Context mContext; // The display device owns its surface, but it should only set it // within a transaction from performTraversalLocked. @@ -53,10 +56,13 @@ abstract class DisplayDevice { // Do not use for any other purpose. DisplayDeviceInfo mDebugLastLoggedDeviceInfo; - public DisplayDevice(DisplayAdapter displayAdapter, IBinder displayToken, String uniqueId) { + public DisplayDevice(DisplayAdapter displayAdapter, IBinder displayToken, String uniqueId, + Context context) { mDisplayAdapter = displayAdapter; mDisplayToken = displayToken; mUniqueId = uniqueId; + mDisplayDeviceConfig = null; + mContext = context; } /** @@ -74,7 +80,10 @@ abstract class DisplayDevice { * @return The DisplayDeviceConfig; {@code null} if not overridden. */ public DisplayDeviceConfig getDisplayDeviceConfig() { - return null; + if (mDisplayDeviceConfig == null) { + mDisplayDeviceConfig = loadDisplayDeviceConfig(); + } + return mDisplayDeviceConfig; } /** @@ -292,4 +301,8 @@ abstract class DisplayDevice { pw.println("mCurrentDisplayRect=" + mCurrentDisplayRect); pw.println("mCurrentSurface=" + mCurrentSurface); } + + private DisplayDeviceConfig loadDisplayDeviceConfig() { + return DisplayDeviceConfig.create(mContext, false); + } } diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 1b25427adf71..49328f1019c3 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -120,7 +120,20 @@ public class DisplayDeviceConfig { // If no config can be loaded from any ddc xml at all, // prepare a whole config using the global config.xml. // Guaranteed not null - if (isDefaultDisplay) { + return create(context, isDefaultDisplay); + } + + /** + * Creates an instance using global values since no display device config xml exists. + * Uses values from config or PowerManager. + * + * @param context + * @param useConfigXml + * @return A configuration instance. + */ + public static DisplayDeviceConfig create(Context context, boolean useConfigXml) { + DisplayDeviceConfig config; + if (useConfigXml) { config = getConfigFromGlobalXml(context); } else { config = getConfigFromPmValues(context); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 6a229417316d..c62dd72ada70 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -249,30 +249,48 @@ public final class DisplayManagerService extends SystemService { /** {@link DisplayBlanker} used by all {@link DisplayPowerController}s. */ private final DisplayBlanker mDisplayBlanker = new DisplayBlanker() { + // Synchronized to avoid race conditions when updating multiple display states. @Override - public void requestDisplayState(int displayId, int state, float brightness) { - // TODO (b/168210494): Stop applying default display state to all displays. - if (displayId != Display.DEFAULT_DISPLAY) { - return; - } - final int[] displayIds; + public synchronized void requestDisplayState(int displayId, int state, float brightness) { + boolean allInactive = true; + boolean allOff = true; + final boolean stateChanged; synchronized (mSyncRoot) { - displayIds = mLogicalDisplayMapper.getDisplayIdsLocked(); + final int index = mDisplayStates.indexOfKey(displayId); + if (index > -1) { + final int currentState = mDisplayStates.valueAt(index); + stateChanged = state != currentState; + if (stateChanged) { + final int size = mDisplayStates.size(); + for (int i = 0; i < size; i++) { + final int displayState = i == index ? state : mDisplayStates.valueAt(i); + if (displayState != Display.STATE_OFF) { + allOff = false; + } + if (Display.isActiveState(displayState)) { + allInactive = false; + } + if (!allOff && !allInactive) { + break; + } + } + } + } else { + stateChanged = false; + } } // The order of operations is important for legacy reasons. if (state == Display.STATE_OFF) { - for (int id : displayIds) { - requestDisplayStateInternal(id, state, brightness); - } + requestDisplayStateInternal(displayId, state, brightness); } - mDisplayPowerCallbacks.onDisplayStateChange(state); + if (stateChanged) { + mDisplayPowerCallbacks.onDisplayStateChange(allInactive, allOff); + } if (state != Display.STATE_OFF) { - for (int id : displayIds) { - requestDisplayStateInternal(id, state, brightness); - } + requestDisplayStateInternal(displayId, state, brightness); } } }; @@ -1160,7 +1178,7 @@ public final class DisplayManagerService extends SystemService { private void handleLogicalDisplayRemovedLocked(@NonNull LogicalDisplay display) { final int displayId = display.getDisplayIdLocked(); - mDisplayPowerControllers.delete(displayId); + mDisplayPowerControllers.removeReturnOld(displayId).stop(); mDisplayStates.delete(displayId); mDisplayBrightnesses.delete(displayId); DisplayManagerGlobal.invalidateLocalDisplayInfoCaches(); @@ -2758,8 +2776,22 @@ public final class DisplayManagerService extends SystemService { 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; } } @@ -2772,13 +2804,6 @@ public final class DisplayManagerService extends SystemService { } @Override - public int getDisplayGroupId(int displayId) { - synchronized (mSyncRoot) { - return mLogicalDisplayMapper.getDisplayGroupIdLocked(displayId); - } - } - - @Override public void registerDisplayGroupListener(DisplayGroupListener listener) { mDisplayGroupListeners.add(listener); } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 9320f5027ce0..62cf86b31180 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -121,6 +121,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final int MSG_SET_TEMPORARY_BRIGHTNESS = 6; private static final int MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT = 7; private static final int MSG_IGNORE_PROXIMITY = 8; + private static final int MSG_STOP = 9; private static final int PROXIMITY_UNKNOWN = -1; private static final int PROXIMITY_NEGATIVE = 0; @@ -351,6 +352,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call @Nullable private final DisplayWhiteBalanceController mDisplayWhiteBalanceController; + @Nullable private final ColorDisplayServiceInternal mCdsi; private final float[] mNitsRange; @@ -409,6 +411,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private ObjectAnimator mColorFadeOffAnimator; private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator; + // True if this DisplayPowerController has been stopped and should no longer be running. + private boolean mStopped; + /** * Creates the display power controller. */ @@ -583,14 +588,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call DisplayWhiteBalanceSettings displayWhiteBalanceSettings = null; DisplayWhiteBalanceController displayWhiteBalanceController = null; - try { - displayWhiteBalanceSettings = new DisplayWhiteBalanceSettings(mContext, mHandler); - displayWhiteBalanceController = DisplayWhiteBalanceFactory.create(mHandler, - mSensorManager, resources); - displayWhiteBalanceSettings.setCallbacks(this); - displayWhiteBalanceController.setCallbacks(this); - } catch (Exception e) { - Slog.e(TAG, "failed to set up display white-balance: " + e); + if (mDisplayId == Display.DEFAULT_DISPLAY) { + try { + displayWhiteBalanceSettings = new DisplayWhiteBalanceSettings(mContext, mHandler); + displayWhiteBalanceController = DisplayWhiteBalanceFactory.create(mHandler, + mSensorManager, resources); + displayWhiteBalanceSettings.setCallbacks(this); + displayWhiteBalanceController.setCallbacks(this); + } catch (Exception e) { + Slog.e(TAG, "failed to set up display white-balance: " + e); + } } mDisplayWhiteBalanceSettings = displayWhiteBalanceSettings; mDisplayWhiteBalanceController = displayWhiteBalanceController; @@ -602,20 +609,24 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mNitsRange = BrightnessMappingStrategy.getFloatArray(context.getResources() .obtainTypedArray(com.android.internal.R.array.config_screenBrightnessNits)); } - mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class); - boolean active = mCdsi.setReduceBrightColorsListener(new ReduceBrightColorsListener() { - @Override - public void onReduceBrightColorsActivationChanged(boolean activated) { - applyReduceBrightColorsSplineAdjustment(); - } + if (mDisplayId == Display.DEFAULT_DISPLAY) { + mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class); + boolean active = mCdsi.setReduceBrightColorsListener(new ReduceBrightColorsListener() { + @Override + public void onReduceBrightColorsActivationChanged(boolean activated) { + applyReduceBrightColorsSplineAdjustment(); + } - @Override - public void onReduceBrightColorsStrengthChanged(int strength) { + @Override + public void onReduceBrightColorsStrengthChanged(int strength) { + applyReduceBrightColorsSplineAdjustment(); + } + }); + if (active) { applyReduceBrightColorsSplineAdjustment(); } - }); - if (active) { - applyReduceBrightColorsSplineAdjustment(); + } else { + mCdsi = null; } } @@ -713,6 +724,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } synchronized (mLock) { + if (mStopped) { + return true; + } + boolean changed = false; if (waitForNegativeProximity @@ -731,11 +746,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (changed) { mDisplayReadyLocked = false; - } - - if (changed && !mPendingRequestChangedLocked) { - mPendingRequestChangedLocked = true; - sendUpdatePowerStateLocked(); + if (!mPendingRequestChangedLocked) { + mPendingRequestChangedLocked = true; + sendUpdatePowerStateLocked(); + } } return mDisplayReadyLocked; @@ -758,6 +772,34 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // TODO: b/175821789 - Support high brightness on multiple (folding) displays } + /** + * Unregisters all listeners and interrupts all running threads; halting future work. + * + * This method should be called when the DisplayPowerController is no longer in use; i.e. when + * the {@link #mDisplayId display} has been removed. + */ + public void stop() { + synchronized (mLock) { + mStopped = true; + Message msg = mHandler.obtainMessage(MSG_STOP); + mHandler.sendMessage(msg); + + if (mDisplayWhiteBalanceController != null) { + mDisplayWhiteBalanceController.setEnabled(false); + } + + if (mAutomaticBrightnessController != null) { + mAutomaticBrightnessController.stop(); + } + + if (mBrightnessTracker != null) { + mBrightnessTracker.stop(); + } + + mContext.getContentResolver().unregisterContentObserver(mSettingsObserver); + } + } + private void sendUpdatePowerState() { synchronized (mLock) { sendUpdatePowerStateLocked(); @@ -765,7 +807,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } private void sendUpdatePowerStateLocked() { - if (!mPendingUpdatePowerStateLocked) { + if (!mStopped && !mPendingUpdatePowerStateLocked) { mPendingUpdatePowerStateLocked = true; Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE); mHandler.sendMessage(msg); @@ -788,7 +830,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mColorFadeOffAnimator.addListener(mAnimatorListener); } - mScreenBrightnessRampAnimator = new RampAnimator<DisplayPowerState>( + mScreenBrightnessRampAnimator = new RampAnimator<>( mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT); mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener); @@ -829,12 +871,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } }; - private final RampAnimator.Listener mRampAnimatorListener = new RampAnimator.Listener() { - @Override - public void onAnimationEnd() { - sendUpdatePowerState(); - } - }; + private final RampAnimator.Listener mRampAnimatorListener = this::sendUpdatePowerState; + + /** Clean up all resources that are accessed via the {@link #mHandler} thread. */ + private void cleanupHandlerThreadAfterStop() { + setProximitySensorEnabled(false); + mHandler.removeCallbacksAndMessages(null); + mPowerState.stop(); + mPowerState = null; + } private void updatePowerState() { // Update the power state request. @@ -844,6 +889,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call int brightnessAdjustmentFlags = 0; mBrightnessReasonTemp.set(null); synchronized (mLock) { + if (mStopped) { + return; + } mPendingUpdatePowerStateLocked = false; if (mPendingRequestLocked == null) { return; // wait until first actual power request @@ -2144,6 +2192,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call case MSG_IGNORE_PROXIMITY: ignoreProximitySensorUntilChangedInternal(); break; + + case MSG_STOP: + cleanupHandlerThreadAfterStop(); + break; } } } diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java index 1d20d878fb81..77aff5b03dd2 100644 --- a/services/core/java/com/android/server/display/DisplayPowerState.java +++ b/services/core/java/com/android/server/display/DisplayPowerState.java @@ -72,6 +72,8 @@ final class DisplayPowerState { private Runnable mCleanListener; + private volatile boolean mStopped; + DisplayPowerState( DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState) { mHandler = new Handler(true /*async*/); @@ -264,9 +266,24 @@ final class DisplayPowerState { } } + /** + * Interrupts all running threads; halting future work. + * + * This method should be called when the DisplayPowerState is no longer in use; i.e. when + * the {@link #mDisplayId display} has been removed. + */ + public void stop() { + mStopped = true; + mPhotonicModulator.interrupt(); + dismissColorFade(); + mCleanListener = null; + mHandler.removeCallbacksAndMessages(null); + } + public void dump(PrintWriter pw) { pw.println(); pw.println("Display Power State:"); + pw.println(" mStopped=" + mStopped); pw.println(" mScreenState=" + Display.stateToString(mScreenState)); pw.println(" mScreenBrightness=" + mScreenBrightness); pw.println(" mScreenReady=" + mScreenReady); @@ -428,7 +445,11 @@ final class DisplayPowerState { if (!stateChanged && !backlightChanged) { try { mLock.wait(); - } catch (InterruptedException ex) { } + } catch (InterruptedException ex) { + if (mStopped) { + return; + } + } continue; } mActualState = state; diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 7ce4f066b058..5b2b3366b117 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -38,8 +38,8 @@ import android.view.DisplayEventReceiver; import android.view.RoundedCorners; import android.view.SurfaceControl; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.display.BrightnessSynchronizer; -import com.android.internal.os.BackgroundThread; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; import com.android.server.lights.LightsManager; @@ -72,6 +72,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { private final Injector mInjector; + private final SurfaceControlProxy mSurfaceControlProxy; // Called with SyncRoot lock held. public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, @@ -79,10 +80,12 @@ final class LocalDisplayAdapter extends DisplayAdapter { this(syncRoot, context, handler, listener, new Injector()); } + @VisibleForTesting LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener, Injector injector) { super(syncRoot, context, handler, listener, TAG); mInjector = injector; + mSurfaceControlProxy = mInjector.getSurfaceControlProxy(); } @Override @@ -92,22 +95,23 @@ final class LocalDisplayAdapter extends DisplayAdapter { mInjector.setDisplayEventListenerLocked(getHandler().getLooper(), new LocalDisplayEventListener()); - for (long physicalDisplayId : SurfaceControl.getPhysicalDisplayIds()) { + for (long physicalDisplayId : mSurfaceControlProxy.getPhysicalDisplayIds()) { tryConnectDisplayLocked(physicalDisplayId); } } private void tryConnectDisplayLocked(long physicalDisplayId) { - final IBinder displayToken = SurfaceControl.getPhysicalDisplayToken(physicalDisplayId); + final IBinder displayToken = + mSurfaceControlProxy.getPhysicalDisplayToken(physicalDisplayId); if (displayToken != null) { SurfaceControl.StaticDisplayInfo staticInfo = - SurfaceControl.getStaticDisplayInfo(displayToken); + mSurfaceControlProxy.getStaticDisplayInfo(displayToken); if (staticInfo == null) { Slog.w(TAG, "No valid static info found for display device " + physicalDisplayId); return; } SurfaceControl.DynamicDisplayInfo dynamicInfo = - SurfaceControl.getDynamicDisplayInfo(displayToken); + mSurfaceControlProxy.getDynamicDisplayInfo(displayToken); if (dynamicInfo == null) { Slog.w(TAG, "No valid dynamic info found for display device " + physicalDisplayId); return; @@ -131,7 +135,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { dynamicInfo.activeColorMode = Display.COLOR_MODE_INVALID; } SurfaceControl.DesiredDisplayModeSpecs modeSpecs = - SurfaceControl.getDesiredDisplayModeSpecs(displayToken); + mSurfaceControlProxy.getDesiredDisplayModeSpecs(displayToken); LocalDisplayDevice device = mDevices.get(physicalDisplayId); if (device == null) { // Display was added. @@ -208,7 +212,6 @@ final class LocalDisplayAdapter extends DisplayAdapter { private SurfaceControl.DisplayMode mActiveSfDisplayMode; private Spline mSystemBrightnessToNits; private Spline mNitsToHalBrightness; - private DisplayDeviceConfig mDisplayDeviceConfig; private DisplayEventReceiver.FrameRateOverride[] mFrameRateOverrides = new DisplayEventReceiver.FrameRateOverride[0]; @@ -217,18 +220,15 @@ final class LocalDisplayAdapter extends DisplayAdapter { SurfaceControl.StaticDisplayInfo staticDisplayInfo, SurfaceControl.DynamicDisplayInfo dynamicInfo, SurfaceControl.DesiredDisplayModeSpecs modeSpecs, boolean isDefaultDisplay) { - super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId); + super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId, + getContext()); mPhysicalDisplayId = physicalDisplayId; mIsDefaultDisplay = isDefaultDisplay; updateDisplayPropertiesLocked(staticDisplayInfo, dynamicInfo, modeSpecs); mSidekickInternal = LocalServices.getService(SidekickInternal.class); - mBacklightAdapter = new BacklightAdapter(displayToken, isDefaultDisplay); - mAllmSupported = SurfaceControl.getAutoLowLatencyModeSupport(displayToken); - mGameContentTypeSupported = SurfaceControl.getGameContentTypeSupport(displayToken); + mBacklightAdapter = new BacklightAdapter(displayToken, isDefaultDisplay, + mSurfaceControlProxy); mDisplayDeviceConfig = null; - // Defer configuration file loading - BackgroundThread.getHandler().sendMessage(PooledLambda.obtainMessage( - LocalDisplayDevice::loadDisplayConfiguration, this)); } @Override @@ -248,6 +248,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { changed |= updateColorModesLocked(dynamicInfo.supportedColorModes, dynamicInfo.activeColorMode); changed |= updateHdrCapabilitiesLocked(dynamicInfo.hdrCapabilities); + changed |= updateAllmSupport(dynamicInfo.autoLowLatencyModeSupported); + changed |= updateGameContentTypeSupport(dynamicInfo.gameContentTypeSupported); if (changed) { mHavePendingChanges = true; @@ -338,19 +340,18 @@ final class LocalDisplayAdapter extends DisplayAdapter { // If we can't map the defaultMode index to a mode, then the physical display // modes must have changed, and the code below for handling changes to the // list of available modes will take care of updating display mode specs. - if (activeBaseMode != NO_DISPLAY_MODE_ID) { - if (mDisplayModeSpecs.baseModeId != activeBaseMode - || mDisplayModeSpecs.primaryRefreshRateRange.min - != modeSpecs.primaryRefreshRateMin - || mDisplayModeSpecs.primaryRefreshRateRange.max - != modeSpecs.primaryRefreshRateMax - || mDisplayModeSpecs.appRequestRefreshRateRange.min - != modeSpecs.appRequestRefreshRateMin - || mDisplayModeSpecs.appRequestRefreshRateRange.max - != modeSpecs.appRequestRefreshRateMax) { - mDisplayModeSpecsInvalid = true; - sendTraversalRequestLocked(); - } + if (activeBaseMode == NO_DISPLAY_MODE_ID + || mDisplayModeSpecs.baseModeId != activeBaseMode + || mDisplayModeSpecs.primaryRefreshRateRange.min + != modeSpecs.primaryRefreshRateMin + || mDisplayModeSpecs.primaryRefreshRateRange.max + != modeSpecs.primaryRefreshRateMax + || mDisplayModeSpecs.appRequestRefreshRateRange.min + != modeSpecs.appRequestRefreshRateMin + || mDisplayModeSpecs.appRequestRefreshRateRange.max + != modeSpecs.appRequestRefreshRateMax) { + mDisplayModeSpecsInvalid = true; + sendTraversalRequestLocked(); } } @@ -408,6 +409,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { @Override public DisplayDeviceConfig getDisplayDeviceConfig() { + if (mDisplayDeviceConfig == null) { + loadDisplayConfiguration(); + } return mDisplayDeviceConfig; } @@ -518,6 +522,22 @@ final class LocalDisplayAdapter extends DisplayAdapter { return true; } + private boolean updateAllmSupport(boolean supported) { + if (mAllmSupported == supported) { + return false; + } + mAllmSupported = supported; + return true; + } + + private boolean updateGameContentTypeSupport(boolean supported) { + if (mGameContentTypeSupported == supported) { + return false; + } + mGameContentTypeSupported = supported; + return true; + } + private SurfaceControl.DisplayMode getModeById(SurfaceControl.DisplayMode[] supportedModes, int modeId) { for (SurfaceControl.DisplayMode mode : supportedModes) { @@ -698,7 +718,12 @@ final class LocalDisplayAdapter extends DisplayAdapter { setDisplayState(Display.STATE_ON); currentState = Display.STATE_ON; } else { - return; // old state and new state is off + if (oldState == Display.STATE_UNKNOWN) { + // There's no guarantee about what the initial state is + // at startup, so we have to set it if previous was UNKNOWN. + setDisplayState(state); + } + return; } } @@ -757,7 +782,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { + "id=" + physicalDisplayId + ", state=" + Display.stateToString(state) + ")"); try { - SurfaceControl.setDisplayPowerMode(token, mode); + mSurfaceControlProxy.setDisplayPowerMode(token, mode); Trace.traceCounter(Trace.TRACE_TAG_POWER, "DisplayPowerMode", mode); } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); @@ -885,9 +910,10 @@ final class LocalDisplayAdapter extends DisplayAdapter { SurfaceControl.DesiredDisplayModeSpecs modeSpecs) { // Do not lock when calling these SurfaceControl methods because they are sync // operations that may block for a while when setting display power mode. - SurfaceControl.setDesiredDisplayModeSpecs(displayToken, modeSpecs); - final int sfActiveModeId = - SurfaceControl.getDynamicDisplayInfo(displayToken).activeDisplayModeId; + mSurfaceControlProxy.setDesiredDisplayModeSpecs(displayToken, modeSpecs); + + final int sfActiveModeId = mSurfaceControlProxy + .getDynamicDisplayInfo(displayToken).activeDisplayModeId; synchronized (getSyncRoot()) { if (updateActiveModeLocked(sfActiveModeId)) { updateDeviceInfoLocked(); @@ -955,7 +981,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { private void requestColorModeAsync(IBinder displayToken, int colorMode) { // Do not lock when calling this SurfaceControl method because it is a sync operation // that may block for a while when setting display power mode. - SurfaceControl.setActiveColorMode(displayToken, colorMode); + mSurfaceControlProxy.setActiveColorMode(displayToken, colorMode); synchronized (getSyncRoot()) { updateDeviceInfoLocked(); } @@ -975,7 +1001,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { return; } - SurfaceControl.setAutoLowLatencyMode(getDisplayTokenLocked(), on); + mSurfaceControlProxy.setAutoLowLatencyMode(getDisplayTokenLocked(), on); } @Override @@ -992,7 +1018,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { return; } - SurfaceControl.setGameContentType(getDisplayTokenLocked(), on); + mSurfaceControlProxy.setGameContentType(getDisplayTokenLocked(), on); } @Override @@ -1136,6 +1162,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { public void setDisplayEventListenerLocked(Looper looper, DisplayEventListener listener) { mReceiver = new ProxyDisplayEventReceiver(looper, listener); } + public SurfaceControlProxy getSurfaceControlProxy() { + return new SurfaceControlProxy(); + } } public interface DisplayEventListener { @@ -1227,10 +1256,65 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } + @VisibleForTesting + static class SurfaceControlProxy { + public SurfaceControl.DynamicDisplayInfo getDynamicDisplayInfo(IBinder token) { + return SurfaceControl.getDynamicDisplayInfo(token); + } + + public long[] getPhysicalDisplayIds() { + return SurfaceControl.getPhysicalDisplayIds(); + } + + public IBinder getPhysicalDisplayToken(long physicalDisplayId) { + return SurfaceControl.getPhysicalDisplayToken(physicalDisplayId); + } + + public SurfaceControl.StaticDisplayInfo getStaticDisplayInfo(IBinder displayToken) { + return SurfaceControl.getStaticDisplayInfo(displayToken); + } + + public SurfaceControl.DesiredDisplayModeSpecs getDesiredDisplayModeSpecs( + IBinder displayToken) { + return SurfaceControl.getDesiredDisplayModeSpecs(displayToken); + } + + public boolean setDesiredDisplayModeSpecs(IBinder token, + SurfaceControl.DesiredDisplayModeSpecs specs) { + return SurfaceControl.setDesiredDisplayModeSpecs(token, specs); + } + + public void setDisplayPowerMode(IBinder displayToken, int mode) { + SurfaceControl.setDisplayPowerMode(displayToken, mode); + } + + public boolean setActiveColorMode(IBinder displayToken, int colorMode) { + return SurfaceControl.setActiveColorMode(displayToken, colorMode); + } + + public void setAutoLowLatencyMode(IBinder displayToken, boolean on) { + SurfaceControl.setAutoLowLatencyMode(displayToken, on); + + } + + public void setGameContentType(IBinder displayToken, boolean on) { + SurfaceControl.setGameContentType(displayToken, on); + } + + public boolean getDisplayBrightnessSupport(IBinder displayToken) { + return SurfaceControl.getDisplayBrightnessSupport(displayToken); + } + + public boolean setDisplayBrightness(IBinder displayToken, float brightness) { + return SurfaceControl.setDisplayBrightness(displayToken, brightness); + } + } + static class BacklightAdapter { private final IBinder mDisplayToken; private final LogicalLight mBacklight; private final boolean mUseSurfaceControlBrightness; + private final SurfaceControlProxy mSurfaceControlProxy; private boolean mForceSurfaceControl = false; @@ -1238,11 +1322,13 @@ final class LocalDisplayAdapter extends DisplayAdapter { * @param displayToken Token for display associated with this backlight. * @param isDefaultDisplay {@code true} if it is the default display. */ - BacklightAdapter(IBinder displayToken, boolean isDefaultDisplay) { + BacklightAdapter(IBinder displayToken, boolean isDefaultDisplay, + SurfaceControlProxy surfaceControlProxy) { mDisplayToken = displayToken; + mSurfaceControlProxy = surfaceControlProxy; - mUseSurfaceControlBrightness = - SurfaceControl.getDisplayBrightnessSupport(mDisplayToken); + mUseSurfaceControlBrightness = mSurfaceControlProxy + .getDisplayBrightnessSupport(mDisplayToken); if (!mUseSurfaceControlBrightness && isDefaultDisplay) { LightsManager lights = LocalServices.getService(LightsManager.class); @@ -1254,7 +1340,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { void setBrightness(float brightness) { if (mUseSurfaceControlBrightness || mForceSurfaceControl) { - SurfaceControl.setDisplayBrightness(mDisplayToken, brightness); + mSurfaceControlProxy.setDisplayBrightness(mDisplayToken, brightness); } else if (mBacklight != null) { mBacklight.setBrightness(brightness); } diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java index a054db533e3f..a3ff534e336e 100644 --- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java +++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java @@ -17,7 +17,6 @@ package com.android.server.display; import android.content.Context; -import android.os.Process; import android.os.SystemProperties; import android.text.TextUtils; import android.util.IndentingPrintWriter; @@ -143,10 +142,6 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { return null; } - public int[] getDisplayIdsLocked() { - return getDisplayIdsLocked(Process.SYSTEM_UID); - } - public int[] getDisplayIdsLocked(int callingUid) { final int count = mLogicalDisplays.size(); int[] displayIds = new int[count]; @@ -171,15 +166,6 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } } - public int getDisplayGroupIdLocked(int displayId) { - final DisplayGroup displayGroup = mDisplayIdToGroupMap.get(displayId); - if (displayGroup != null) { - return displayGroup.getGroupId(); - } - - return -1; - } - public DisplayGroup getDisplayGroupLocked(int groupId) { final int size = mDisplayIdToGroupMap.size(); for (int i = 0; i < size; i++) { diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java index 69943e3904ed..330379cf58eb 100644 --- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java +++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java @@ -281,7 +281,8 @@ final class OverlayDisplayAdapter extends DisplayAdapter { List<OverlayMode> modes, int activeMode, int defaultMode, float refreshRate, long presentationDeadlineNanos, OverlayFlags flags, int state, SurfaceTexture surfaceTexture, int number) { - super(OverlayDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + number); + super(OverlayDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + number, + getContext()); mName = name; mRefreshRate = refreshRate; mDisplayPresentationDeadlineNanos = presentationDeadlineNanos; diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java index ff4717b7131b..52a810bd8caa 100644 --- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java +++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java @@ -236,7 +236,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter { int ownerUid, String ownerPackageName, Surface surface, int flags, Callback callback, String uniqueId, int uniqueIndex, VirtualDisplayConfig virtualDisplayConfig) { - super(VirtualDisplayAdapter.this, displayToken, uniqueId); + super(VirtualDisplayAdapter.this, displayToken, uniqueId, getContext()); mAppToken = appToken; mOwnerUid = ownerUid; mOwnerPackageName = ownerPackageName; diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java index 57323170b327..d2baaf2228a1 100644 --- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java +++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java @@ -598,7 +598,8 @@ final class WifiDisplayAdapter extends DisplayAdapter { public WifiDisplayDevice(IBinder displayToken, String name, int width, int height, float refreshRate, int flags, String address, Surface surface) { - super(WifiDisplayAdapter.this, displayToken, DISPLAY_NAME_PREFIX + address); + super(WifiDisplayAdapter.this, displayToken, DISPLAY_NAME_PREFIX + address, + getContext()); mName = name; mWidth = width; mHeight = height; diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java index 86a8e36d748d..983b6b5bceb4 100644 --- a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java +++ b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java @@ -100,9 +100,9 @@ final class DeviceSelectAction extends HdmiCecFeatureAction { @Override public boolean start() { - if (mIsCec20) { - sendSetStreamPath(); - } + // Wake-up on <Set Stream Path> was not mandatory before CEC 2.0. + // The message is re-sent at the end of the action for devices that don't support 2.0. + sendSetStreamPath(); int targetPowerStatus = localDevice().mService.getHdmiCecNetwork() .getCecDeviceInfo(getTargetAddress()).getDevicePowerStatus(); if (!mIsCec20 || targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) { diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java index f64efe7c26cc..624af30854dc 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java @@ -39,6 +39,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ConcurrentUtils; import com.android.server.hdmi.cec.config.CecSettings; import com.android.server.hdmi.cec.config.Setting; import com.android.server.hdmi.cec.config.Value; @@ -55,7 +56,9 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.Executor; import javax.xml.datatype.DatatypeConfigurationException; @@ -99,7 +102,7 @@ public class HdmiCecConfig { private final Object mLock = new Object(); @GuardedBy("mLock") - private final ArrayMap<Setting, Set<SettingChangeListener>> + private final ArrayMap<Setting, ArrayMap<SettingChangeListener, Executor>> mSettingChangeListeners = new ArrayMap<>(); private SettingsObserver mSettingsObserver; @@ -292,7 +295,7 @@ public class HdmiCecConfig { case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED: return STORAGE_GLOBAL_SETTINGS; case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION: - return STORAGE_GLOBAL_SETTINGS; + return STORAGE_SHARED_PREFS; case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE: return STORAGE_GLOBAL_SETTINGS; case HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE: @@ -329,7 +332,7 @@ public class HdmiCecConfig { case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED: return Global.HDMI_CONTROL_ENABLED; case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION: - return Global.HDMI_CEC_VERSION; + return setting.getName(); case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE: return Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP; case HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE: @@ -402,9 +405,6 @@ public class HdmiCecConfig { case Global.HDMI_CONTROL_ENABLED: notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED); break; - case Global.HDMI_CEC_VERSION: - notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION); - break; case Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP: notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE); break; @@ -430,12 +430,20 @@ public class HdmiCecConfig { private void notifySettingChanged(@NonNull Setting setting) { synchronized (mLock) { - Set<SettingChangeListener> listeners = mSettingChangeListeners.get(setting); + ArrayMap<SettingChangeListener, Executor> listeners = + mSettingChangeListeners.get(setting); if (listeners == null) { return; // No listeners registered, do nothing. } - for (SettingChangeListener listener: listeners) { - listener.onChange(setting.getName()); + for (Entry<SettingChangeListener, Executor> entry: listeners.entrySet()) { + SettingChangeListener listener = entry.getKey(); + Executor executor = entry.getValue(); + executor.execute(new Runnable() { + @Override + public void run() { + listener.onChange(setting.getName()); + } + }); } } } @@ -450,7 +458,6 @@ public class HdmiCecConfig { ContentResolver resolver = mContext.getContentResolver(); String[] settings = new String[] { Global.HDMI_CONTROL_ENABLED, - Global.HDMI_CEC_VERSION, Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, @@ -471,10 +478,19 @@ public class HdmiCecConfig { } /** - * Register change listener for a given setting name. + * Register change listener for a given setting name using DirectExecutor. */ public void registerChangeListener(@NonNull @CecSettingName String name, SettingChangeListener listener) { + registerChangeListener(name, listener, ConcurrentUtils.DIRECT_EXECUTOR); + } + + /** + * Register change listener for a given setting name and executor. + */ + public void registerChangeListener(@NonNull @CecSettingName String name, + SettingChangeListener listener, + Executor executor) { Setting setting = getSetting(name); if (setting == null) { throw new IllegalArgumentException("Setting '" + name + "' does not exist."); @@ -486,9 +502,9 @@ public class HdmiCecConfig { } synchronized (mLock) { if (!mSettingChangeListeners.containsKey(setting)) { - mSettingChangeListeners.put(setting, new HashSet<>()); + mSettingChangeListeners.put(setting, new ArrayMap<>()); } - mSettingChangeListeners.get(setting).add(listener); + mSettingChangeListeners.get(setting).put(listener, executor); } } @@ -503,7 +519,8 @@ public class HdmiCecConfig { } synchronized (mLock) { if (mSettingChangeListeners.containsKey(setting)) { - Set<SettingChangeListener> listeners = mSettingChangeListeners.get(setting); + ArrayMap<SettingChangeListener, Executor> listeners = + mSettingChangeListeners.get(setting); listeners.remove(listener); if (listeners.isEmpty()) { mSettingChangeListeners.remove(setting); diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index d014f149e831..b4d9b01f716f 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -108,6 +108,7 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.Executor; import java.util.stream.Collectors; /** @@ -199,6 +200,13 @@ public class HdmiControlService extends SystemService { public @interface WakeReason { } + private final Executor mServiceThreadExecutor = new Executor() { + @Override + public void execute(Runnable r) { + runOnServiceThread(r); + } + }; + // Logical address of the active source. @GuardedBy("mLock") protected final ActiveSource mActiveSource = new ActiveSource(); @@ -520,14 +528,14 @@ public class HdmiControlService extends SystemService { HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED); setControlEnabled(enabled); } - }); + }, mServiceThreadExecutor); mHdmiCecConfig.registerChangeListener(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, new HdmiCecConfig.SettingChangeListener() { @Override public void onChange(String setting) { initializeCec(INITIATED_BY_ENABLE_CEC); } - }); + }, mServiceThreadExecutor); mHdmiCecConfig.registerChangeListener( HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY, new HdmiCecConfig.SettingChangeListener() { @@ -537,7 +545,7 @@ public class HdmiControlService extends SystemService { setCecOption(OptionKey.WAKEUP, tv().getAutoWakeup()); } } - }); + }, mServiceThreadExecutor); } private void bootCompleted() { diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java index a7f34ed85e0d..4c4c9783fab0 100644 --- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java +++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java @@ -259,7 +259,7 @@ final class HotplugDetectionAction extends HdmiCecFeatureAction { } private void mayDisableSystemAudioAndARC(int address) { - if (HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, address)) { + if (!HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, address)) { return; } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index c5be20e39864..bbe52bcecea2 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -17,6 +17,7 @@ package com.android.server.input; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -51,6 +52,8 @@ import android.hardware.input.InputManagerInternal.LidSwitchCallback; import android.hardware.input.InputSensorInfo; import android.hardware.input.KeyboardLayout; import android.hardware.input.TouchCalibration; +import android.hardware.lights.Light; +import android.hardware.lights.LightState; import android.media.AudioManager; import android.os.Binder; import android.os.Bundle; @@ -237,9 +240,21 @@ public class InputManagerService extends IInputManager.Stub // List of vibrator states by device id. @GuardedBy("mVibratorLock") private final SparseBooleanArray mIsVibrating = new SparseBooleanArray(); + private Object mLightLock = new Object(); + // State for light tokens. A light token marks a lights manager session, it is generated + // by light session open() and deleted in session close(). + // When lights session requests light states, the token will be used to find the light session. + @GuardedBy("mLightLock") + private final ArrayMap<IBinder, LightSession> mLightSessions = + new ArrayMap<IBinder, LightSession>(); // State for lid switch + // Lock for the lid switch state. Held when triggering callbacks to guarantee lid switch events + // are delivered in order. For ex, when a new lid switch callback is registered the lock is held + // while the callback is processing the initial lid switch event which guarantees that any + // events that occur at the same time are delivered after the callback has returned. private final Object mLidSwitchLock = new Object(); + @GuardedBy("mLidSwitchLock") private List<LidSwitchCallback> mLidSwitchCallbacks = new ArrayList<>(); // State for the currently installed input filter. @@ -303,6 +318,12 @@ public class InputManagerService extends IInputManager.Stub private static native int[] nativeGetVibratorIds(long ptr, int deviceId); private static native int nativeGetBatteryCapacity(long ptr, int deviceId); private static native int nativeGetBatteryStatus(long ptr, int deviceId); + private static native List<Light> nativeGetLights(long ptr, int deviceId); + private static native int nativeGetLightPlayerId(long ptr, int deviceId, int lightId); + private static native int nativeGetLightColor(long ptr, int deviceId, int lightId); + private static native void nativeSetLightPlayerId(long ptr, int deviceId, int lightId, + int playerId); + private static native void nativeSetLightColor(long ptr, int deviceId, int lightId, int color); private static native void nativeReloadKeyboardLayouts(long ptr); private static native void nativeReloadDeviceAliases(long ptr); private static native String nativeDump(long ptr); @@ -387,9 +408,6 @@ public class InputManagerService extends IInputManager.Stub public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER; public static final int SW_MUTE_DEVICE_BIT = 1 << SW_MUTE_DEVICE; - /** Indicates an open state for the lid switch. */ - public static final int SW_STATE_LID_OPEN = 0; - /** Whether to use the dev/input/event or uevent subsystem for the audio jack. */ final boolean mUseDevInputEventForAudioJack; @@ -425,13 +443,18 @@ public class InputManagerService extends IInputManager.Stub } void registerLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) { - boolean lidOpen; synchronized (mLidSwitchLock) { mLidSwitchCallbacks.add(callback); - lidOpen = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID) - == SW_STATE_LID_OPEN; + + // Skip triggering the initial callback if the system is not yet ready as the switch + // state will be reported as KEY_STATE_UNKNOWN. The callback will be triggered in + // systemRunning(). + if (mSystemReady) { + boolean lidOpen = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID) + == KEY_STATE_UP; + callback.notifyLidSwitchChanged(0 /* whenNanos */, lidOpen); + } } - callback.notifyLidSwitchChanged(0 /* whenNanos */, lidOpen); } void unregisterLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) { @@ -479,7 +502,18 @@ public class InputManagerService extends IInputManager.Stub } mNotificationManager = (NotificationManager)mContext.getSystemService( Context.NOTIFICATION_SERVICE); - mSystemReady = true; + + synchronized (mLidSwitchLock) { + mSystemReady = true; + + // Send the initial lid switch state to any callback registered before the system was + // ready. + int switchState = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID); + for (int i = 0; i < mLidSwitchCallbacks.size(); i++) { + LidSwitchCallback callback = mLidSwitchCallbacks.get(i); + callback.notifyLidSwitchChanged(0 /* whenNanos */, switchState == KEY_STATE_UP); + } + } IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); @@ -2294,6 +2328,151 @@ public class InputManagerService extends IInputManager.Stub } } + /** + * LightSession represents a light session for lights manager. + */ + private final class LightSession implements DeathRecipient { + private final int mDeviceId; + private final IBinder mToken; + private final String mOpPkg; + // The light ids and states that are requested by the light seesion + private int[] mLightIds; + private LightState[] mLightStates; + + LightSession(int deviceId, String opPkg, IBinder token) { + mDeviceId = deviceId; + mOpPkg = opPkg; + mToken = token; + } + + @Override + public void binderDied() { + if (DEBUG) { + Slog.d(TAG, "Light token died."); + } + synchronized (mLightLock) { + closeLightSession(mDeviceId, mToken); + mLightSessions.remove(mToken); + } + } + } + + /** + * Returns the lights available for apps to control on the specified input device. + * Only lights that aren't reserved for system use are available to apps. + */ + @Override // Binder call + public List<Light> getLights(int deviceId) { + return nativeGetLights(mPtr, deviceId); + } + + /** + * Set specified light state with for a specific input device. + */ + private void setLightStateInternal(int deviceId, Light light, LightState lightState) { + Preconditions.checkNotNull(light, "light does not exist"); + if (DEBUG) { + Slog.d(TAG, "setLightStateInternal device " + deviceId + " light " + light + + "lightState " + lightState); + } + if (light.getType() == Light.LIGHT_TYPE_INPUT_PLAYER_ID) { + nativeSetLightPlayerId(mPtr, deviceId, light.getId(), lightState.getPlayerId()); + } else if (light.getType() == Light.LIGHT_TYPE_INPUT_SINGLE + || light.getType() == Light.LIGHT_TYPE_INPUT_RGB) { + // Set ARGB format color to input device light + // Refer to https://developer.android.com/reference/kotlin/android/graphics/Color + nativeSetLightColor(mPtr, deviceId, light.getId(), lightState.getColor()); + } else { + Slog.e(TAG, "setLightStates for unsupported light type " + light.getType()); + } + } + + /** + * Set multiple light states with multiple light ids for a specific input device. + */ + private void setLightStatesInternal(int deviceId, int[] lightIds, LightState[] lightStates) { + final List<Light> lights = nativeGetLights(mPtr, deviceId); + SparseArray<Light> lightArray = new SparseArray<>(); + for (int i = 0; i < lights.size(); i++) { + lightArray.put(lights.get(i).getId(), lights.get(i)); + } + for (int i = 0; i < lightIds.length; i++) { + if (lightArray.contains(lightIds[i])) { + setLightStateInternal(deviceId, lightArray.get(lightIds[i]), lightStates[i]); + } + } + } + + /** + * Set states for multiple lights for an opened light session. + */ + @Override + public void setLightStates(int deviceId, int[] lightIds, LightState[] lightStates, + IBinder token) { + Preconditions.checkArgument(lightIds.length == lightStates.length, + "lights and light states are not same length"); + synchronized (mLightLock) { + LightSession lightSession = mLightSessions.get(token); + Preconditions.checkArgument(lightSession != null, "not registered"); + Preconditions.checkState(lightSession.mDeviceId == deviceId, "Incorrect device ID"); + lightSession.mLightIds = lightIds.clone(); + lightSession.mLightStates = lightStates.clone(); + if (DEBUG) { + Slog.d(TAG, "setLightStates for " + lightSession.mOpPkg + " device " + deviceId); + } + } + setLightStatesInternal(deviceId, lightIds, lightStates); + } + + @Override + public @Nullable LightState getLightState(int deviceId, int lightId) { + synchronized (mLightLock) { + int color = nativeGetLightColor(mPtr, deviceId, lightId); + int playerId = nativeGetLightPlayerId(mPtr, deviceId, lightId); + + return new LightState(color, playerId); + } + } + + @Override + public void openLightSession(int deviceId, String opPkg, IBinder token) { + Preconditions.checkNotNull(token); + synchronized (mLightLock) { + Preconditions.checkState(mLightSessions.get(token) == null, "already registered"); + LightSession lightSession = new LightSession(deviceId, opPkg, token); + try { + token.linkToDeath(lightSession, 0); + } catch (RemoteException ex) { + // give up + ex.rethrowAsRuntimeException(); + } + mLightSessions.put(token, lightSession); + if (DEBUG) { + Slog.d(TAG, "Open light session for " + opPkg + " device " + deviceId); + } + } + } + + @Override + public void closeLightSession(int deviceId, IBinder token) { + Preconditions.checkNotNull(token); + synchronized (mLightLock) { + LightSession lightSession = mLightSessions.get(token); + Preconditions.checkState(lightSession != null, "not registered"); + // Turn off the lights that were previously requested by the session to be closed. + Arrays.fill(lightSession.mLightStates, new LightState(0)); + setLightStatesInternal(deviceId, lightSession.mLightIds, + lightSession.mLightStates); + mLightSessions.remove(token); + // If any other session is still pending with light request, apply the first session's + // request. + if (!mLightSessions.isEmpty()) { + LightSession nextSession = mLightSessions.valueAt(0); + setLightStatesInternal(deviceId, nextSession.mLightIds, nextSession.mLightStates); + } + } + } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; @@ -2379,14 +2558,13 @@ public class InputManagerService extends IInputManager.Stub if ((switchMask & SW_LID_BIT) != 0) { final boolean lidOpen = ((switchValues & SW_LID_BIT) == 0); - - ArrayList<LidSwitchCallback> callbacksCopy; synchronized (mLidSwitchLock) { - callbacksCopy = new ArrayList<>(mLidSwitchCallbacks); - } - for (int i = 0; i < callbacksCopy.size(); i++) { - LidSwitchCallback callbacks = callbacksCopy.get(i); - callbacks.notifyLidSwitchChanged(whenNanos, lidOpen); + if (mSystemReady) { + for (int i = 0; i < mLidSwitchCallbacks.size(); i++) { + LidSwitchCallback callbacks = mLidSwitchCallbacks.get(i); + callbacks.notifyLidSwitchChanged(whenNanos, lidOpen); + } + } } } diff --git a/services/core/java/com/android/server/input/OWNERS b/services/core/java/com/android/server/input/OWNERS index 0313a40f7270..82c6ee12c7ae 100644 --- a/services/core/java/com/android/server/input/OWNERS +++ b/services/core/java/com/android/server/input/OWNERS @@ -1,2 +1,3 @@ +lzye@google.com michaelwr@google.com svv@google.com diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 0754df0e6b9f..1e6658930840 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -2620,8 +2620,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken); // Dispatch display id for InputMethodService to update context display. - executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO( - MSG_INITIALIZE_IME, mCurTokenDisplayId, mCurMethod, mCurToken)); + executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOOO( + MSG_INITIALIZE_IME, mCurTokenDisplayId, mCurMethod, mCurToken, + mMethodMap.get(mCurMethodId).getConfigChanges())); scheduleNotifyImeUidToAudioService(mCurMethodUid); if (mCurClient != null) { clearClientSessionLocked(mCurClient); @@ -4466,7 +4467,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } final IBinder token = (IBinder) args.arg2; ((IInputMethod) args.arg1).initializeInternal(token, msg.arg1, - new InputMethodPrivilegedOperationsImpl(this, token)); + new InputMethodPrivilegedOperationsImpl(this, token), + (int) args.arg3); } catch (RemoteException e) { } args.recycle(); diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index fd0b9454cfc1..cbbc981d9ed6 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -252,7 +252,7 @@ public class LocationManagerService extends ILocationManager.Stub { // @GuardedBy("mProviderManagers") // hold lock for writes, no lock necessary for simple reads - private final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers = + final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers = new CopyOnWriteArrayList<>(); LocationManagerService(Context context, Injector injector, LocationEventLog eventLog) { @@ -284,7 +284,7 @@ public class LocationManagerService extends ILocationManager.Stub { } @Nullable - private LocationProviderManager getLocationProviderManager(String providerName) { + LocationProviderManager getLocationProviderManager(String providerName) { if (providerName == null) { return null; } @@ -496,7 +496,7 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void startGnssBatch(long periodNanos, ILocationListener listener, String packageName, - String attributionTag, String listenerId) { + @Nullable String attributionTag, String listenerId) { mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null); if (mGnssManagerService == null) { @@ -633,7 +633,7 @@ public class LocationManagerService extends ILocationManager.Stub { @Nullable @Override public ICancellationSignal getCurrentLocation(String provider, LocationRequest request, - ILocationCallback consumer, String packageName, String attributionTag, + ILocationCallback consumer, String packageName, @Nullable String attributionTag, String listenerId) { CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, listenerId); @@ -657,7 +657,7 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void registerLocationListener(String provider, LocationRequest request, ILocationListener listener, String packageName, @Nullable String attributionTag, - @Nullable String listenerId) { + String listenerId) { CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, listenerId); int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(), @@ -808,7 +808,7 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public Location getLastLocation(String provider, LastLocationRequest request, - String packageName, String attributionTag) { + String packageName, @Nullable String attributionTag) { CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag); int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(), identity.getPid()); @@ -875,9 +875,10 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void registerGnssStatusCallback(IGnssStatusListener listener, String packageName, - String attributionTag) { + @Nullable String attributionTag, String listenerId) { if (mGnssManagerService != null) { - mGnssManagerService.registerGnssStatusCallback(listener, packageName, attributionTag); + mGnssManagerService.registerGnssStatusCallback(listener, packageName, attributionTag, + listenerId); } } @@ -890,9 +891,10 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void registerGnssNmeaCallback(IGnssNmeaListener listener, String packageName, - String attributionTag) { + @Nullable String attributionTag, String listenerId) { if (mGnssManagerService != null) { - mGnssManagerService.registerGnssNmeaCallback(listener, packageName, attributionTag); + mGnssManagerService.registerGnssNmeaCallback(listener, packageName, attributionTag, + listenerId); } } @@ -904,11 +906,12 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public void addGnssMeasurementsListener(@Nullable GnssMeasurementRequest request, - IGnssMeasurementsListener listener, String packageName, String attributionTag) { + public void addGnssMeasurementsListener(GnssMeasurementRequest request, + IGnssMeasurementsListener listener, String packageName, @Nullable String attributionTag, + String listenerId) { if (mGnssManagerService != null) { mGnssManagerService.addGnssMeasurementsListener(request, listener, packageName, - attributionTag); + attributionTag, listenerId); } } @@ -954,10 +957,10 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void addGnssNavigationMessageListener(IGnssNavigationMessageListener listener, - String packageName, String attributionTag) { + String packageName, @Nullable String attributionTag, String listenerId) { if (mGnssManagerService != null) { mGnssManagerService.addGnssNavigationMessageListener(listener, packageName, - attributionTag); + attributionTag, listenerId); } } @@ -1285,13 +1288,13 @@ public class LocationManagerService extends ILocationManager.Stub { ipw.println("Historical Aggregate Location Provider Data:"); ipw.increaseIndent(); - ArrayMap<String, ArrayMap<String, LocationEventLog.AggregateStats>> aggregateStats = + ArrayMap<String, ArrayMap<CallerIdentity, LocationEventLog.AggregateStats>> aggregateStats = mEventLog.copyAggregateStats(); for (int i = 0; i < aggregateStats.size(); i++) { ipw.print(aggregateStats.keyAt(i)); ipw.println(":"); ipw.increaseIndent(); - ArrayMap<String, LocationEventLog.AggregateStats> providerStats = + ArrayMap<CallerIdentity, LocationEventLog.AggregateStats> providerStats = aggregateStats.valueAt(i); for (int j = 0; j < providerStats.size(); j++) { ipw.print(providerStats.keyAt(j)); @@ -1359,7 +1362,7 @@ public class LocationManagerService extends ILocationManager.Stub { if (provider != null && !provider.equals(manager.getName())) { continue; } - if (identity.equalsIgnoringListenerId(manager.getIdentity())) { + if (identity.equals(manager.getIdentity())) { return true; } } diff --git a/services/core/java/com/android/server/location/countrydetector/ComprehensiveCountryDetector.java b/services/core/java/com/android/server/location/countrydetector/ComprehensiveCountryDetector.java index af3907e78432..dda502b2b9aa 100644 --- a/services/core/java/com/android/server/location/countrydetector/ComprehensiveCountryDetector.java +++ b/services/core/java/com/android/server/location/countrydetector/ComprehensiveCountryDetector.java @@ -320,13 +320,15 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase { "(source: " + detectedCountry.getSource() + ", countryISO: " + detectedCountry.getCountryIso() + ")") + " isAirplaneModeOff()=" + isAirplaneModeOff() + + " isWifiOn()=" + isWifiOn() + " mListener=" + mListener + " isGeoCoderImplemnted()=" + isGeoCoderImplemented()); } if (startLocationBasedDetection && (detectedCountry == null || detectedCountry.getSource() > Country.COUNTRY_SOURCE_LOCATION) - && isAirplaneModeOff() && mListener != null && isGeoCoderImplemented()) { + && (isAirplaneModeOff() || isWifiOn()) && mListener != null + && isGeoCoderImplemented()) { if (DEBUG) Slog.d(TAG, "run startLocationBasedDetector()"); // Start finding location when the source is less reliable than the // location and the airplane mode is off (as geocoder will not @@ -387,6 +389,11 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase { mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) == 0; } + protected boolean isWifiOn() { + return Settings.Global.getInt( + mContext.getContentResolver(), Settings.Global.WIFI_ON, 0) != 0; + } + /** * Notify the country change. */ diff --git a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java index dbfd0a52b013..29ce3783c4a1 100644 --- a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java +++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java @@ -64,16 +64,17 @@ public class LocationEventLog extends LocalEventLog { private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 10; @GuardedBy("mAggregateStats") - private final ArrayMap<String, ArrayMap<String, AggregateStats>> mAggregateStats; + private final ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> mAggregateStats; public LocationEventLog() { super(getLogSize()); mAggregateStats = new ArrayMap<>(4); } - public ArrayMap<String, ArrayMap<String, AggregateStats>> copyAggregateStats() { + /** Copies out all aggregated stats. */ + public ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> copyAggregateStats() { synchronized (mAggregateStats) { - ArrayMap<String, ArrayMap<String, AggregateStats>> copy = new ArrayMap<>( + ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> copy = new ArrayMap<>( mAggregateStats); for (int i = 0; i < copy.size(); i++) { copy.setValueAt(i, new ArrayMap<>(copy.valueAt(i))); @@ -82,17 +83,18 @@ public class LocationEventLog extends LocalEventLog { } } - private AggregateStats getAggregateStats(String provider, String packageName) { + private AggregateStats getAggregateStats(String provider, CallerIdentity identity) { synchronized (mAggregateStats) { - ArrayMap<String, AggregateStats> packageMap = mAggregateStats.get(provider); + ArrayMap<CallerIdentity, AggregateStats> packageMap = mAggregateStats.get(provider); if (packageMap == null) { packageMap = new ArrayMap<>(2); mAggregateStats.put(provider, packageMap); } - AggregateStats stats = packageMap.get(packageName); + CallerIdentity stripped = identity.stripListenerId(); + AggregateStats stats = packageMap.get(stripped); if (stats == null) { stats = new AggregateStats(); - packageMap.put(packageName, stats); + packageMap.put(stripped, stats); } return stats; } @@ -117,34 +119,33 @@ public class LocationEventLog extends LocalEventLog { public void logProviderClientRegistered(String provider, CallerIdentity identity, LocationRequest request) { addLogEvent(EVENT_PROVIDER_REGISTER_CLIENT, provider, identity, request); - getAggregateStats(provider, identity.getPackageName()) - .markRequestAdded(request.getIntervalMillis()); + getAggregateStats(provider, identity).markRequestAdded(request.getIntervalMillis()); } /** Logs a client unregistration for a location provider. */ public void logProviderClientUnregistered(String provider, CallerIdentity identity) { addLogEvent(EVENT_PROVIDER_UNREGISTER_CLIENT, provider, identity); - getAggregateStats(provider, identity.getPackageName()).markRequestRemoved(); + getAggregateStats(provider, identity).markRequestRemoved(); } /** Logs a client for a location provider entering the active state. */ public void logProviderClientActive(String provider, CallerIdentity identity) { - getAggregateStats(provider, identity.getPackageName()).markRequestActive(); + getAggregateStats(provider, identity).markRequestActive(); } /** Logs a client for a location provider leaving the active state. */ public void logProviderClientInactive(String provider, CallerIdentity identity) { - getAggregateStats(provider, identity.getPackageName()).markRequestInactive(); + getAggregateStats(provider, identity).markRequestInactive(); } /** Logs a client for a location provider entering the foreground state. */ public void logProviderClientForeground(String provider, CallerIdentity identity) { - getAggregateStats(provider, identity.getPackageName()).markRequestForeground(); + getAggregateStats(provider, identity).markRequestForeground(); } /** Logs a client for a location provider leaving the foreground state. */ public void logProviderClientBackground(String provider, CallerIdentity identity) { - getAggregateStats(provider, identity.getPackageName()).markRequestBackground(); + getAggregateStats(provider, identity).markRequestBackground(); } /** Logs a change to the provider request for a location provider. */ @@ -165,7 +166,7 @@ public class LocationEventLog extends LocalEventLog { if (Build.IS_DEBUGGABLE || D) { addLogEvent(EVENT_PROVIDER_DELIVER_LOCATION, provider, numLocations, identity); } - getAggregateStats(provider, identity.getPackageName()).markLocationDelivered(); + getAggregateStats(provider, identity).markLocationDelivered(); } /** Logs that a provider has entered or exited stationary throttling. */ @@ -218,7 +219,7 @@ public class LocationEventLog extends LocalEventLog { protected final String mProvider; - protected ProviderEvent(long timeDelta, String provider) { + ProviderEvent(long timeDelta, String provider) { super(timeDelta); mProvider = provider; } @@ -234,7 +235,7 @@ public class LocationEventLog extends LocalEventLog { private final int mUserId; private final boolean mEnabled; - protected ProviderEnabledEvent(long timeDelta, String provider, int userId, + ProviderEnabledEvent(long timeDelta, String provider, int userId, boolean enabled) { super(timeDelta, provider); mUserId = userId; @@ -252,7 +253,7 @@ public class LocationEventLog extends LocalEventLog { private final boolean mMocked; - protected ProviderMockedEvent(long timeDelta, String provider, boolean mocked) { + ProviderMockedEvent(long timeDelta, String provider, boolean mocked) { super(timeDelta, provider); mMocked = mocked; } @@ -273,7 +274,7 @@ public class LocationEventLog extends LocalEventLog { private final CallerIdentity mIdentity; @Nullable private final LocationRequest mLocationRequest; - private ProviderRegisterEvent(long timeDelta, String provider, boolean registered, + ProviderRegisterEvent(long timeDelta, String provider, boolean registered, CallerIdentity identity, @Nullable LocationRequest locationRequest) { super(timeDelta, provider); mRegistered = registered; @@ -296,7 +297,7 @@ public class LocationEventLog extends LocalEventLog { private final ProviderRequest mRequest; - private ProviderUpdateEvent(long timeDelta, String provider, ProviderRequest request) { + ProviderUpdateEvent(long timeDelta, String provider, ProviderRequest request) { super(timeDelta, provider); mRequest = request; } @@ -311,7 +312,7 @@ public class LocationEventLog extends LocalEventLog { private final int mNumLocations; - private ProviderReceiveLocationEvent(long timeDelta, String provider, int numLocations) { + ProviderReceiveLocationEvent(long timeDelta, String provider, int numLocations) { super(timeDelta, provider); mNumLocations = numLocations; } @@ -327,7 +328,7 @@ public class LocationEventLog extends LocalEventLog { private final int mNumLocations; @Nullable private final CallerIdentity mIdentity; - private ProviderDeliverLocationEvent(long timeDelta, String provider, int numLocations, + ProviderDeliverLocationEvent(long timeDelta, String provider, int numLocations, @Nullable CallerIdentity identity) { super(timeDelta, provider); mNumLocations = numLocations; @@ -345,7 +346,7 @@ public class LocationEventLog extends LocalEventLog { private final boolean mStationaryThrottled; - private ProviderStationaryThrottledEvent(long timeDelta, String provider, + ProviderStationaryThrottledEvent(long timeDelta, String provider, boolean stationaryThrottled) { super(timeDelta, provider); mStationaryThrottled = stationaryThrottled; @@ -363,7 +364,7 @@ public class LocationEventLog extends LocalEventLog { @LocationPowerSaveMode private final int mLocationPowerSaveMode; - private LocationPowerSaveModeEvent(long timeDelta, + LocationPowerSaveModeEvent(long timeDelta, @LocationPowerSaveMode int locationPowerSaveMode) { super(timeDelta); mLocationPowerSaveMode = locationPowerSaveMode; @@ -401,7 +402,7 @@ public class LocationEventLog extends LocalEventLog { private final int mUserId; private final boolean mEnabled; - private LocationEnabledEvent(long timeDelta, int userId, boolean enabled) { + LocationEnabledEvent(long timeDelta, int userId, boolean enabled) { super(timeDelta); mUserId = userId; mEnabled = enabled; diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java index b6695c20bd97..8312c6361835 100644 --- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java +++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java @@ -60,7 +60,7 @@ public class GnssManagerService { private static final String ATTRIBUTION_ID = "GnssService"; - private final Context mContext; + final Context mContext; private final GnssNative mGnssNative; private final GnssLocationProvider mGnssLocationProvider; @@ -154,10 +154,11 @@ public class GnssManagerService { * Registers listener for GNSS status changes. */ public void registerGnssStatusCallback(IGnssStatusListener listener, String packageName, - @Nullable String attributionTag) { + @Nullable String attributionTag, String listenerId) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); - CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag); + CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, + listenerId); mGnssStatusProvider.addListener(identity, listener); } @@ -172,10 +173,11 @@ public class GnssManagerService { * Registers listener for GNSS NMEA messages. */ public void registerGnssNmeaCallback(IGnssNmeaListener listener, String packageName, - @Nullable String attributionTag) { + @Nullable String attributionTag, String listenerId) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); - CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag); + CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, + listenerId); mGnssNmeaProvider.addListener(identity, listener); } @@ -191,12 +193,13 @@ public class GnssManagerService { */ public void addGnssMeasurementsListener(GnssMeasurementRequest request, IGnssMeasurementsListener listener, String packageName, - @Nullable String attributionTag) { + @Nullable String attributionTag, String listenerId) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); if (request.isCorrelationVectorOutputsEnabled()) { mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null); } - CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag); + CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, + listenerId); mGnssMeasurementsProvider.addListener(request, identity, listener); } @@ -223,10 +226,11 @@ public class GnssManagerService { * Adds a GNSS navigation message listener. */ public void addGnssNavigationMessageListener(IGnssNavigationMessageListener listener, - String packageName, @Nullable String attributionTag) { + String packageName, @Nullable String attributionTag, String listenerId) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); - CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag); + CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, + listenerId); mGnssNavigationMessageProvider.addListener(identity, listener); } diff --git a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java index 4e0a0b89f238..08deb8698608 100644 --- a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java +++ b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java @@ -196,9 +196,12 @@ public abstract class AbstractLocationProvider { */ protected AbstractLocationProvider(Executor executor, @Nullable CallerIdentity identity, @Nullable ProviderProperties properties) { + Preconditions.checkArgument(identity == null || identity.getListenerId() == null); mExecutor = executor; mInternalState = new AtomicReference<>(new InternalState(null, - State.EMPTY_STATE.withIdentity(identity).withProperties(properties))); + State.EMPTY_STATE + .withIdentity(identity) + .withProperties(properties))); mController = new Controller(); } @@ -273,6 +276,7 @@ public abstract class AbstractLocationProvider { * Call this method to report a change in provider packages. */ protected void setIdentity(@Nullable CallerIdentity identity) { + Preconditions.checkArgument(identity == null || identity.getListenerId() == null); setState(state -> state.withIdentity(identity)); } @@ -335,6 +339,8 @@ public abstract class AbstractLocationProvider { private boolean mStarted = false; + Controller() {} + @Override public State setListener(@Nullable Listener listener) { InternalState oldInternalState = mInternalState.getAndUpdate( diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java index 388b5a4dd54e..5a35d7f88ff3 100644 --- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java @@ -304,6 +304,7 @@ public class LocationProviderManager extends LocationTransport transport, @PermissionLevel int permissionLevel) { super(Objects.requireNonNull(request), identity, transport); + Preconditions.checkArgument(identity.getListenerId() != null); Preconditions.checkArgument(permissionLevel > PERMISSION_NONE); Preconditions.checkArgument(!request.getWorkSource().isEmpty()); diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java index 30ea5556b41c..7e00fd69a148 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java @@ -194,7 +194,9 @@ class RebootEscrowManager { } public void reportMetric(boolean success) { - FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, success); + // TODO(b/179105110) design error code; and report the true value for other fields. + FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, 0, 1, 1, + -1, 0); } public RebootEscrowEventLog getEventLog() { diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java index 676f4218f745..4b4146683a09 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java +++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java @@ -29,6 +29,7 @@ import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.os.Process.INVALID_UID; import android.app.ActivityManager; +import android.app.ActivityManager.ProcessCapability; import android.net.NetworkPolicyManager; import android.os.UserHandle; import android.util.Log; @@ -97,13 +98,15 @@ public class NetworkPolicyLogger { } } - void uidStateChanged(int uid, int procState, long procStateSeq) { + void uidStateChanged(int uid, int procState, long procStateSeq, + @ProcessCapability int capability) { synchronized (mLock) { if (LOGV || uid == mDebugUid) { Slog.v(TAG, uid + " state changed to " - + ProcessList.makeProcStateString(procState) + " with seq=" + procStateSeq); + + ProcessList.makeProcStateString(procState) + ",seq=" + procStateSeq + + ",cap=" + ActivityManager.getCapabilitiesSummary(capability)); } - mUidStateChangeBuffer.uidStateChanged(uid, procState, procStateSeq); + mUidStateChangeBuffer.uidStateChanged(uid, procState, procStateSeq, capability); } } @@ -373,7 +376,8 @@ public class NetworkPolicyLogger { super(Data.class, capacity); } - public void uidStateChanged(int uid, int procState, long procStateSeq) { + public void uidStateChanged(int uid, int procState, long procStateSeq, + @ProcessCapability int capability) { final Data data = getNextSlot(); if (data == null) return; @@ -381,6 +385,7 @@ public class NetworkPolicyLogger { data.type = EVENT_UID_STATE_CHANGED; data.ifield1 = uid; data.ifield2 = procState; + data.ifield3 = capability; data.lfield1 = procStateSeq; data.timeStamp = System.currentTimeMillis(); } @@ -546,8 +551,9 @@ public class NetworkPolicyLogger { case EVENT_NETWORK_BLOCKED: return data.ifield1 + "-" + getBlockedReason(data.ifield2); case EVENT_UID_STATE_CHANGED: - return data.ifield1 + "-" + ProcessList.makeProcStateString(data.ifield2) - + "-" + data.lfield1; + return data.ifield1 + ":" + ProcessList.makeProcStateString(data.ifield2) + + ":" + ActivityManager.getCapabilitiesSummary(data.ifield3) + + ":" + data.lfield1; case EVENT_POLICIES_CHANGED: return getPolicyChangedLog(data.ifield1, data.ifield2, data.ifield3); case EVENT_METEREDNESS_CHANGED: diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index aa7da54b2e1d..2b9dd2d84ac6 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -131,6 +131,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityManager.ProcessCapability; import android.app.ActivityManagerInternal; import android.app.AppGlobals; import android.app.AppOpsManager; @@ -166,6 +167,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkIdentity; import android.net.NetworkPolicy; import android.net.NetworkPolicyManager; +import android.net.NetworkPolicyManager.UidState; import android.net.NetworkQuotaInfo; import android.net.NetworkRequest; import android.net.NetworkSpecifier; @@ -557,7 +559,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** Foreground at UID granularity. */ @GuardedBy("mUidRulesFirstLock") - final SparseIntArray mUidState = new SparseIntArray(); + final SparseArray<UidState> mUidState = new SparseArray<UidState>(); /** Map from network ID to last observed meteredness state */ @GuardedBy("mNetworkPoliciesSecondLock") @@ -880,9 +882,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); try { + // TODO: There shouldn't be a need to receive callback for all changes. mActivityManager.registerUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE|ActivityManager.UID_OBSERVER_GONE, - NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE, "android"); + ActivityManager.PROCESS_STATE_UNKNOWN, "android"); mNetworkManager.registerObserver(mAlertObserver); } catch (RemoteException e) { // ignored; both services live in system_server @@ -988,9 +991,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final private IUidObserver mUidObserver = new IUidObserver.Stub() { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq, - int capability) { + @ProcessCapability int capability) { + // TODO: Avoid creating a new UidStateCallbackInfo object every time + // we get a callback for an uid mUidEventHandler.obtainMessage(UID_MSG_STATE_CHANGED, - uid, procState, procStateSeq).sendToTarget(); + new UidStateCallbackInfo(uid, procState, procStateSeq, capability)) + .sendToTarget(); } @Override public void onUidGone(int uid, boolean disabled) { @@ -1007,6 +1013,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } }; + private static final class UidStateCallbackInfo { + public final int uid; + public final int procState; + public final long procStateSeq; + @ProcessCapability + public final int capability; + + UidStateCallbackInfo(int uid, int procState, long procStateSeq, + @ProcessCapability int capability) { + this.uid = uid; + this.procState = procState; + this.procStateSeq = procStateSeq; + this.capability = capability; + } + } + final private BroadcastReceiver mPowerSaveWhitelistReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -3718,14 +3740,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { fout.print("UID="); fout.print(uid); - final int state = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); - fout.print(" state="); - fout.print(state); - if (state <= ActivityManager.PROCESS_STATE_TOP) { - fout.print(" (fg)"); + final UidState uidState = mUidState.get(uid); + if (uidState == null) { + fout.print(" state={null}"); } else { - fout.print(state <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE - ? " (fg svc)" : " (bg)"); + fout.print(" state="); + fout.print(uidState.toString()); } final int uidRules = mUidRules.get(uid, RULE_NONE); @@ -3783,26 +3803,20 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @VisibleForTesting boolean isUidForeground(int uid) { synchronized (mUidRulesFirstLock) { - return isUidStateForeground( - mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY)); + return isProcStateAllowedWhileIdleOrPowerSaveMode(mUidState.get(uid)); } } @GuardedBy("mUidRulesFirstLock") private boolean isUidForegroundOnRestrictBackgroundUL(int uid) { - final int procState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); - return isProcStateAllowedWhileOnRestrictBackground(procState); + final UidState uidState = mUidState.get(uid); + return isProcStateAllowedWhileOnRestrictBackground(uidState); } @GuardedBy("mUidRulesFirstLock") private boolean isUidForegroundOnRestrictPowerUL(int uid) { - final int procState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); - return isProcStateAllowedWhileIdleOrPowerSaveMode(procState); - } - - private boolean isUidStateForeground(int state) { - // only really in foreground when screen is also on - return state <= NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE; + final UidState uidState = mUidState.get(uid); + return isProcStateAllowedWhileIdleOrPowerSaveMode(uidState); } /** @@ -3811,16 +3825,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * {@link #updateRulesForPowerRestrictionsUL(int)}. Returns true if the state was updated. */ @GuardedBy("mUidRulesFirstLock") - private boolean updateUidStateUL(int uid, int uidState) { + private boolean updateUidStateUL(int uid, int procState, @ProcessCapability int capability) { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateUidStateUL"); try { - final int oldUidState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); - if (oldUidState != uidState) { + final UidState oldUidState = mUidState.get(uid); + if (oldUidState == null || oldUidState.procState != procState + || oldUidState.capability != capability) { + final UidState newUidState = new UidState(uid, procState, capability); // state changed, push updated rules - mUidState.put(uid, uidState); - updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState, uidState); + mUidState.put(uid, newUidState); + updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState, newUidState); if (isProcStateAllowedWhileIdleOrPowerSaveMode(oldUidState) - != isProcStateAllowedWhileIdleOrPowerSaveMode(uidState) ) { + != isProcStateAllowedWhileIdleOrPowerSaveMode(newUidState)) { updateRuleForAppIdleUL(uid); if (mDeviceIdleMode) { updateRuleForDeviceIdleUL(uid); @@ -3842,11 +3858,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private boolean removeUidStateUL(int uid) { final int index = mUidState.indexOfKey(uid); if (index >= 0) { - final int oldUidState = mUidState.valueAt(index); + final UidState oldUidState = mUidState.valueAt(index); mUidState.removeAt(index); - if (oldUidState != ActivityManager.PROCESS_STATE_CACHED_EMPTY) { - updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState, - ActivityManager.PROCESS_STATE_CACHED_EMPTY); + if (oldUidState != null) { + updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState, null); if (mDeviceIdleMode) { updateRuleForDeviceIdleUL(uid); } @@ -3873,8 +3888,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - private void updateRestrictBackgroundRulesOnUidStatusChangedUL(int uid, int oldUidState, - int newUidState) { + private void updateRestrictBackgroundRulesOnUidStatusChangedUL(int uid, + @Nullable UidState oldUidState, @Nullable UidState newUidState) { final boolean oldForeground = isProcStateAllowedWhileOnRestrictBackground(oldUidState); final boolean newForeground = @@ -4979,11 +4994,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { public boolean handleMessage(Message msg) { switch (msg.what) { case UID_MSG_STATE_CHANGED: { - final int uid = msg.arg1; - final int procState = msg.arg2; - final long procStateSeq = (Long) msg.obj; - - handleUidChanged(uid, procState, procStateSeq); + final UidStateCallbackInfo uidStateCallbackInfo = + (UidStateCallbackInfo) msg.obj; + final int uid = uidStateCallbackInfo.uid; + final int procState = uidStateCallbackInfo.procState; + final long procStateSeq = uidStateCallbackInfo.procStateSeq; + final int capability = uidStateCallbackInfo.capability; + + handleUidChanged(uid, procState, procStateSeq, capability); return true; } case UID_MSG_GONE: { @@ -4998,23 +5016,24 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } }; - void handleUidChanged(int uid, int procState, long procStateSeq) { + void handleUidChanged(int uid, int procState, long procStateSeq, + @ProcessCapability int capability) { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "onUidStateChanged"); try { boolean updated; synchronized (mUidRulesFirstLock) { // We received a uid state change callback, add it to the history so that it // will be useful for debugging. - mLogger.uidStateChanged(uid, procState, procStateSeq); + mLogger.uidStateChanged(uid, procState, procStateSeq, capability); // Now update the network policy rules as per the updated uid state. - updated = updateUidStateUL(uid, procState); + updated = updateUidStateUL(uid, procState, capability); // Updating the network rules is done, so notify AMS about this. mActivityManagerInternal.notifyNetworkPolicyRulesUpdated(uid, procStateSeq); } // Do this without the lock held. handleUidChanged() and handleUidGone() are // called from the handler, so there's no multi-threading issue. if (updated) { - updateNetworkStats(uid, isUidStateForeground(procState)); + updateNetworkStats(uid, isProcStateAllowedWhileOnRestrictBackground(procState)); } } finally { Trace.traceEnd(Trace.TRACE_TAG_NETWORK); @@ -5349,6 +5368,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + private static void collectKeys(SparseArray<UidState> source, SparseBooleanArray target) { + final int size = source.size(); + for (int i = 0; i < size; i++) { + target.put(source.keyAt(i), true); + } + } + @Override public void factoryReset(String subscriber) { mContext.enforceCallingOrSelfPermission(NETWORK_SETTINGS, TAG); diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java index 160d2daab6a2..afb47e831bdb 100644 --- a/services/core/java/com/android/server/notification/NotificationDelegate.java +++ b/services/core/java/com/android/server/notification/NotificationDelegate.java @@ -32,7 +32,7 @@ public interface NotificationDelegate { void onNotificationActionClick(int callingUid, int callingPid, String key, int actionIndex, Notification.Action action, NotificationVisibility nv, boolean generatedByAssistant); void onNotificationClear(int callingUid, int callingPid, - String pkg, String tag, int id, int userId, String key, + String pkg, int userId, String key, @NotificationStats.DismissalSurface int dismissalSurface, @NotificationStats.DismissalSentiment int dismissalSentiment, NotificationVisibility nv); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 8b4c6392fec0..f8cb2e4cecfc 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1044,15 +1044,19 @@ public class NotificationManagerService extends SystemService { @Override public void onNotificationClear(int callingUid, int callingPid, - String pkg, String tag, int id, int userId, String key, + String pkg, int userId, String key, @NotificationStats.DismissalSurface int dismissalSurface, @NotificationStats.DismissalSentiment int dismissalSentiment, NotificationVisibility nv) { + String tag = null; + int id = 0; synchronized (mNotificationLock) { NotificationRecord r = mNotificationsByKey.get(key); if (r != null) { r.recordDismissalSurface(dismissalSurface); r.recordDismissalSentiment(dismissalSentiment); + tag = r.getSbn().getTag(); + id = r.getSbn().getId(); } } cancelNotification(callingUid, callingPid, pkg, tag, id, 0, diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java index f078242a659f..4500bbcd250f 100644 --- a/services/core/java/com/android/server/notification/SnoozeHelper.java +++ b/services/core/java/com/android/server/notification/SnoozeHelper.java @@ -39,6 +39,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.util.XmlUtils; +import com.android.server.pm.PackageManagerService; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -465,6 +466,7 @@ public class SnoozeHelper { return PendingIntent.getBroadcast(mContext, REQUEST_CODE_REPOST, new Intent(REPOST_ACTION) + .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME) .setData(new Uri.Builder().scheme(REPOST_SCHEME).appendPath(key).build()) .addFlags(Intent.FLAG_RECEIVER_FOREGROUND) .putExtra(EXTRA_KEY, key) diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java index d95a7254efe1..9c4c5101cb6c 100644 --- a/services/core/java/com/android/server/os/NativeTombstoneManager.java +++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java @@ -425,7 +425,7 @@ public final class NativeTombstoneManager { } } stream.end(token); - + break; case (int) Tombstone.SELINUX_LABEL: selinuxLabel = stream.readString(Tombstone.SELINUX_LABEL); diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index 5d7c41c7b08f..1acbabda9e19 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -69,6 +69,7 @@ import java.util.Objects; import java.util.Set; import java.util.StringTokenizer; import java.util.concurrent.Executor; +import java.util.function.Function; /** * The entity responsible for filtering visibility between apps based on declarations in their @@ -1354,14 +1355,13 @@ public class AppsFilter implements Watchable, Snappable { } public void dumpQueries( - PrintWriter pw, PackageManagerService pms, @Nullable Integer filteringAppId, - DumpState dumpState, - int[] users) { + PrintWriter pw, @Nullable Integer filteringAppId, DumpState dumpState, int[] users, + Function<Integer, String[]> getPackagesForUid) { final SparseArray<String> cache = new SparseArray<>(); ToString<Integer> expandPackages = input -> { String cachedValue = cache.get(input); if (cachedValue == null) { - final String[] packagesForUid = pms.getPackagesForUid(input); + final String[] packagesForUid = getPackagesForUid.apply(input); if (packagesForUid == null) { cachedValue = "[unknown app id " + input + "]"; } else { diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java index 308e815d7659..9a9b14c31314 100644 --- a/services/core/java/com/android/server/pm/DataLoaderManagerService.java +++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java @@ -171,7 +171,7 @@ public class DataLoaderManagerService extends SystemService { } } - private class DataLoaderServiceConnection implements ServiceConnection { + private class DataLoaderServiceConnection implements ServiceConnection, IBinder.DeathRecipient { final int mId; final IDataLoaderStatusListener mListener; IDataLoader mDataLoader; @@ -194,6 +194,13 @@ public class DataLoaderManagerService extends SystemService { mContext.unbindService(this); return; } + try { + service.linkToDeath(this, /*flags=*/0); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to link to DataLoader's death: " + mId, e); + onBindingDied(className); + return; + } callListener(IDataLoaderStatusListener.DATA_LOADER_BOUND); } @@ -218,6 +225,13 @@ public class DataLoaderManagerService extends SystemService { destroy(); } + @Override + public void binderDied() { + Slog.i(TAG, "DataLoader " + mId + " died"); + callListener(IDataLoaderStatusListener.DATA_LOADER_DESTROYED); + destroy(); + } + IDataLoader getDataLoader() { return mDataLoader; } diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java index 2a1fc87fc29f..380cdb10569b 100644 --- a/services/core/java/com/android/server/pm/DumpState.java +++ b/services/core/java/com/android/server/pm/DumpState.java @@ -55,6 +55,9 @@ public final class DumpState { private int mOptions; private boolean mTitlePrinted; + private boolean mFullPreferred; + + private String mTargetPackageName; private SharedUserSetting mSharedUser; @@ -99,4 +102,20 @@ public final class DumpState { public void setSharedUser(SharedUserSetting user) { mSharedUser = user; } + + public String getTargetPackageName() { + return mTargetPackageName; + } + + public void setTargetPackageName(String packageName) { + mTargetPackageName = packageName; + } + + public boolean isFullPreferred() { + return mFullPreferred; + } + + public void setFullPreferred(boolean fullPreferred) { + mFullPreferred = fullPreferred; + } } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index b9e3e0f4450b..0a443f3fd7f9 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -780,7 +780,8 @@ public class PackageDexOptimizer { return getOatDir(codePath).getAbsolutePath(); } - static File getOatDir(File codePath) { + /** Returns the oat dir for the given code path */ + public static File getOatDir(File codePath) { return new File(codePath, OAT_DIR_NAME); } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 9e2ca9d32315..7bf3c5c1f4c9 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -1207,8 +1207,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private void computeProgressLocked(boolean forcePublish) { - mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f) - + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f); + if (!mCommitted) { + mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f) + + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f); + } else { + // For incremental installs, continue publishing the install progress during committing. + mProgress = mIncrementalProgress; + } // Only publish when meaningful change if (forcePublish || Math.abs(mProgress - mReportedProgress) >= 0.01) { @@ -1944,9 +1949,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed"); } - // Client staging is fully done at this point - mClientProgress = 1f; - computeProgressLocked(true); + if (!isIncrementalInstallation()) { + // For non-incremental installs, client staging is fully done at this point + mClientProgress = 1f; + computeProgressLocked(true); + } // This ongoing commit should keep session active, even though client // will probably close their end. @@ -3804,6 +3811,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { public void onPackageLoadingProgressChanged(float progress) { synchronized (mLock) { mIncrementalProgress = progress; + computeProgressLocked(true); } } }); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 2a3feb123dd9..16966d4de4e6 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -369,6 +369,7 @@ import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.Settings.DatabaseVersion; import com.android.server.pm.Settings.VersionInfo; import com.android.server.pm.dex.ArtManagerService; +import com.android.server.pm.dex.ArtUtils; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DexoptOptions; import com.android.server.pm.dex.PackageDexUsage; @@ -1994,6 +1995,7 @@ public class PackageManagerService extends IPackageManager.Stub SigningDetails getSigningDetails(int uid); boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId); boolean filterAppAccess(String packageName, int callingUid, int userId); + void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState); } /** @@ -2037,6 +2039,9 @@ public class PackageManagerService extends IPackageManager.Stub private final InstantAppResolverConnection mInstantAppResolverConnection; private final DefaultAppProvider mDefaultAppProvider; private final DomainVerificationManagerInternal mDomainVerificationManager; + private final PackageDexOptimizer mPackageDexOptimizer; + private final DexManager mDexManager; + private final CompilerStats mCompilerStats; // PackageManagerService attributes that are primitives are referenced through the // pms object directly. Primitives are the only attributes so referenced. @@ -2083,6 +2088,9 @@ public class PackageManagerService extends IPackageManager.Stub mInstantAppResolverConnection = args.service.mInstantAppResolverConnection; mDefaultAppProvider = args.service.mDefaultAppProvider; mDomainVerificationManager = args.service.mDomainVerificationManager; + mPackageDexOptimizer = args.service.mPackageDexOptimizer; + mDexManager = args.service.mDexManager; + mCompilerStats = args.service.mCompilerStats; // Used to reference PMS attributes that are primitives and which are not // updated under control of the PMS lock. @@ -4393,6 +4401,143 @@ public class PackageManagerService extends IPackageManager.Stub userId); } + public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) { + final String packageName = dumpState.getTargetPackageName(); + + switch (type) { + case DumpState.DUMP_VERSION: + { + if (dumpState.onTitlePrinted()) { + pw.println(); + } + pw.println("Database versions:"); + mSettings.dumpVersionLPr(new IndentingPrintWriter(pw, " ")); + break; + } + + case DumpState.DUMP_PREFERRED_XML: + { + pw.flush(); + FileOutputStream fout = new FileOutputStream(fd); + BufferedOutputStream str = new BufferedOutputStream(fout); + TypedXmlSerializer serializer = Xml.newFastSerializer(); + try { + serializer.setOutput(str, StandardCharsets.UTF_8.name()); + serializer.startDocument(null, true); + serializer.setFeature( + "http://xmlpull.org/v1/doc/features.html#indent-output", true); + mSettings.writePreferredActivitiesLPr(serializer, 0, + dumpState.isFullPreferred()); + serializer.endDocument(); + serializer.flush(); + } catch (IllegalArgumentException e) { + pw.println("Failed writing: " + e); + } catch (IllegalStateException e) { + pw.println("Failed writing: " + e); + } catch (IOException e) { + pw.println("Failed writing: " + e); + } + break; + } + + case DumpState.DUMP_QUERIES: + { + final PackageSetting setting = mSettings.getPackageLPr(packageName); + Integer filteringAppId = setting == null ? null : setting.appId; + mAppsFilter.dumpQueries( + pw, filteringAppId, dumpState, mUserManager.getUserIds(), + this::getPackagesForUid); + break; + } + + case DumpState.DUMP_DOMAIN_PREFERRED: + { + final android.util.IndentingPrintWriter writer = + new android.util.IndentingPrintWriter(pw); + if (dumpState.onTitlePrinted()) pw.println(); + + writer.println("Domain verification status:"); + writer.increaseIndent(); + try { + mDomainVerificationManager.printState(writer, packageName, + UserHandle.USER_ALL, mSettings::getPackageLPr); + } catch (PackageManager.NameNotFoundException e) { + pw.println("Failure printing domain verification information"); + Slog.e(TAG, "Failure printing domain verification information", e); + } + writer.decreaseIndent(); + break; + } + + case DumpState.DUMP_DEXOPT: + { + final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + ipw.println(); + ipw.println("Dexopt state:"); + ipw.increaseIndent(); + Collection<PackageSetting> pkgSettings; + if (packageName != null) { + PackageSetting targetPkgSetting = mSettings.getPackageLPr(packageName); + if (targetPkgSetting != null) { + pkgSettings = Collections.singletonList(targetPkgSetting); + } else { + ipw.println("Unable to find package: " + packageName); + return; + } + } else { + pkgSettings = mSettings.getPackagesLocked().values(); + } + + for (PackageSetting pkgSetting : pkgSettings) { + final AndroidPackage pkg = pkgSetting.getPkg(); + if (pkg == null) { + continue; + } + ipw.println("[" + pkgSetting.name + "]"); + ipw.increaseIndent(); + mPackageDexOptimizer.dumpDexoptState(ipw, pkg, pkgSetting, + mDexManager.getPackageUseInfoOrDefault(pkg.getPackageName())); + ipw.decreaseIndent(); + } + break; + } + + case DumpState.DUMP_COMPILER_STATS: + { + final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + ipw.println(); + ipw.println("Compiler stats:"); + ipw.increaseIndent(); + Collection<AndroidPackage> packages; + if (packageName != null) { + AndroidPackage targetPackage = mPackages.get(packageName); + if (targetPackage != null) { + packages = Collections.singletonList(targetPackage); + } else { + ipw.println("Unable to find package: " + packageName); + return; + } + } else { + packages = mPackages.values(); + } + + for (AndroidPackage pkg : packages) { + final String pkgName = pkg.getPackageName(); + ipw.println("[" + pkgName + "]"); + ipw.increaseIndent(); + + CompilerStats.PackageStats stats = mCompilerStats.getPackageStats(pkgName); + if (stats == null) { + ipw.println("(No recorded stats)"); + } else { + stats.dump(ipw); + } + ipw.decreaseIndent(); + } + break; + } + } // switch + } } /** @@ -23554,10 +23699,8 @@ public class PackageManagerService extends IPackageManager.Stub if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return; DumpState dumpState = new DumpState(); - boolean fullPreferred = false; boolean checkin = false; - String packageName = null; ArraySet<String> permissionNames = null; int opti = 0; @@ -23626,7 +23769,7 @@ public class PackageManagerService extends IPackageManager.Stub opti++; // Is this a package name? if ("android".equals(cmd) || cmd.contains(".")) { - packageName = cmd; + dumpState.setTargetPackageName(cmd); // When dumping a single package, we always dump all of its // filter information since the amount of data will be reasonable. dumpState.setOptionEnabled(DumpState.OPTION_SHOW_FILTERS); @@ -23707,7 +23850,7 @@ public class PackageManagerService extends IPackageManager.Stub } else if ("preferred-xml".equals(cmd)) { dumpState.setDump(DumpState.DUMP_PREFERRED_XML); if (opti < args.length && "--full".equals(args[opti])) { - fullPreferred = true; + dumpState.setFullPreferred(true); opti++; } } else if ("d".equals(cmd) || "domain-preferred-apps".equals(cmd)) { @@ -23760,257 +23903,208 @@ public class PackageManagerService extends IPackageManager.Stub } } + final String packageName = dumpState.getTargetPackageName(); if (checkin) { pw.println("vers,1"); } // reader - synchronized (mLock) { - if (dumpState.isDumping(DumpState.DUMP_VERSION) && packageName == null) { - if (!checkin) { - if (dumpState.onTitlePrinted()) - pw.println(); - pw.println("Database versions:"); - mSettings.dumpVersionLPr(new IndentingPrintWriter(pw, " ")); - } + if (dumpState.isDumping(DumpState.DUMP_VERSION) && packageName == null) { + if (!checkin) { + dump(DumpState.DUMP_VERSION, fd, pw, dumpState); } + } - if (!checkin - && dumpState.isDumping(DumpState.DUMP_KNOWN_PACKAGES) - && packageName == null) { - if (dumpState.onTitlePrinted()) { - pw.println(); - } - final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); - ipw.println("Known Packages:"); + if (!checkin + && dumpState.isDumping(DumpState.DUMP_KNOWN_PACKAGES) + && packageName == null) { + if (dumpState.onTitlePrinted()) { + pw.println(); + } + final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); + ipw.println("Known Packages:"); + ipw.increaseIndent(); + for (int i = 0; i <= LAST_KNOWN_PACKAGE; i++) { + final String knownPackage = mPmInternal.knownPackageToString(i); + ipw.print(knownPackage); + ipw.println(":"); + final String[] pkgNames = mPmInternal.getKnownPackageNames(i, + UserHandle.USER_SYSTEM); ipw.increaseIndent(); - for (int i = 0; i <= LAST_KNOWN_PACKAGE; i++) { - final String knownPackage = mPmInternal.knownPackageToString(i); - ipw.print(knownPackage); - ipw.println(":"); - final String[] pkgNames = mPmInternal.getKnownPackageNames(i, - UserHandle.USER_SYSTEM); - ipw.increaseIndent(); - if (ArrayUtils.isEmpty(pkgNames)) { - ipw.println("none"); - } else { - for (String name : pkgNames) { - ipw.println(name); - } + if (ArrayUtils.isEmpty(pkgNames)) { + ipw.println("none"); + } else { + for (String name : pkgNames) { + ipw.println(name); } - ipw.decreaseIndent(); } ipw.decreaseIndent(); } + ipw.decreaseIndent(); + } + + if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) { + final String requiredVerifierPackage = mRequiredVerifierPackage; + if (!checkin) { + if (dumpState.onTitlePrinted()) { + pw.println(); + } + pw.println("Verifiers:"); + pw.print(" Required: "); + pw.print(requiredVerifierPackage); + pw.print(" (uid="); + pw.print(getPackageUid(requiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING, + UserHandle.USER_SYSTEM)); + pw.println(")"); + } else if (requiredVerifierPackage != null) { + pw.print("vrfy,"); pw.print(requiredVerifierPackage); + pw.print(","); + pw.println(getPackageUid(requiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING, + UserHandle.USER_SYSTEM)); + } + } - if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) { + if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER) && packageName == null) { + final DomainVerificationProxy proxy = mDomainVerificationManager.getProxy(); + final ComponentName verifierComponent = proxy.getComponentName(); + if (verifierComponent != null) { + String verifierPackageName = verifierComponent.getPackageName(); if (!checkin) { if (dumpState.onTitlePrinted()) pw.println(); - pw.println("Verifiers:"); - pw.print(" Required: "); - pw.print(mRequiredVerifierPackage); + pw.println("Domain Verifier:"); + pw.print(" Using: "); + pw.print(verifierPackageName); pw.print(" (uid="); - pw.print(getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING, + pw.print(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM)); pw.println(")"); - } else if (mRequiredVerifierPackage != null) { - pw.print("vrfy,"); pw.print(mRequiredVerifierPackage); + } else if (verifierPackageName != null) { + pw.print("dv,"); pw.print(verifierPackageName); pw.print(","); - pw.println(getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING, + pw.println(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM)); } + } else { + pw.println(); + pw.println("No Domain Verifier available!"); } + } - if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER) && - packageName == null) { - DomainVerificationProxy proxy = mDomainVerificationManager.getProxy(); - ComponentName verifierComponent = proxy.getComponentName(); - if (verifierComponent != null) { - String verifierPackageName = verifierComponent.getPackageName(); - if (!checkin) { - if (dumpState.onTitlePrinted()) - pw.println(); - pw.println("Domain Verifier:"); - pw.print(" Using: "); - pw.print(verifierPackageName); - pw.print(" (uid="); - pw.print(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING, - UserHandle.USER_SYSTEM)); - pw.println(")"); - } else if (verifierPackageName != null) { - pw.print("dv,"); pw.print(verifierPackageName); - pw.print(","); - pw.println(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING, - UserHandle.USER_SYSTEM)); - } - } else { - pw.println(); - pw.println("No Domain Verifier available!"); - } + if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) { + // TODO: Move it to ComputerEngine once LongSparseArray<SharedLibraryInfo> is copied + // in snapshot. + synchronized (mLock) { + dumpSharedLibrariesLPr(pw, dumpState, checkin); } + } - if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) { - boolean printedHeader = false; - final int numSharedLibraries = mSharedLibraries.size(); - for (int index = 0; index < numSharedLibraries; index++) { - final String libName = mSharedLibraries.keyAt(index); - WatchedLongSparseArray<SharedLibraryInfo> versionedLib - = mSharedLibraries.get(libName); - if (versionedLib == null) { - continue; - } - final int versionCount = versionedLib.size(); - for (int i = 0; i < versionCount; i++) { - SharedLibraryInfo libraryInfo = versionedLib.valueAt(i); - if (!checkin) { - if (!printedHeader) { - if (dumpState.onTitlePrinted()) - pw.println(); - pw.println("Libraries:"); - printedHeader = true; - } - pw.print(" "); - } else { - pw.print("lib,"); - } - pw.print(libraryInfo.getName()); - if (libraryInfo.isStatic()) { - pw.print(" version=" + libraryInfo.getLongVersion()); - } - if (!checkin) { - pw.print(" -> "); - } - if (libraryInfo.getPath() != null) { - if (libraryInfo.isNative()) { - pw.print(" (so) "); - } else { - pw.print(" (jar) "); - } - pw.print(libraryInfo.getPath()); - } else { - pw.print(" (apk) "); - pw.print(libraryInfo.getPackageName()); - } - pw.println(); - } - } + if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) { + if (dumpState.onTitlePrinted()) { + pw.println(); + } + if (!checkin) { + pw.println("Features:"); } - if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) { - if (dumpState.onTitlePrinted()) - pw.println(); - if (!checkin) { - pw.println("Features:"); - } - - synchronized (mAvailableFeatures) { - for (FeatureInfo feat : mAvailableFeatures.values()) { - if (checkin) { - pw.print("feat,"); - pw.print(feat.name); - pw.print(","); - pw.println(feat.version); - } else { - pw.print(" "); - pw.print(feat.name); - if (feat.version > 0) { - pw.print(" version="); - pw.print(feat.version); - } - pw.println(); + synchronized (mAvailableFeatures) { + for (FeatureInfo feat : mAvailableFeatures.values()) { + if (checkin) { + pw.print("feat,"); + pw.print(feat.name); + pw.print(","); + pw.println(feat.version); + } else { + pw.print(" "); + pw.print(feat.name); + if (feat.version > 0) { + pw.print(" version="); + pw.print(feat.version); } + pw.println(); } } } + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) { + if (!checkin && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) { + synchronized (mLock) { mComponentResolver.dumpActivityResolvers(pw, dumpState, packageName); } - if (!checkin && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) { + } + if (!checkin && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) { + synchronized (mLock) { mComponentResolver.dumpReceiverResolvers(pw, dumpState, packageName); } - if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) { + } + if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) { + synchronized (mLock) { mComponentResolver.dumpServiceResolvers(pw, dumpState, packageName); } - if (!checkin && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) { + } + if (!checkin && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) { + synchronized (mLock) { mComponentResolver.dumpProviderResolvers(pw, dumpState, packageName); } + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) { + if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) { + // TODO: This cannot be moved to ComputerEngine since some variables with collections + // types in IntentResolver such as mTypeToFilter do not have a copy of `F[]`. + synchronized (mLock) { mSettings.dumpPreferred(pw, dumpState, packageName); } + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) { - pw.flush(); - FileOutputStream fout = new FileOutputStream(fd); - BufferedOutputStream str = new BufferedOutputStream(fout); - TypedXmlSerializer serializer = Xml.newFastSerializer(); - try { - serializer.setOutput(str, StandardCharsets.UTF_8.name()); - serializer.startDocument(null, true); - serializer.setFeature( - "http://xmlpull.org/v1/doc/features.html#indent-output", true); - mSettings.writePreferredActivitiesLPr(serializer, 0, fullPreferred); - serializer.endDocument(); - serializer.flush(); - } catch (IllegalArgumentException e) { - pw.println("Failed writing: " + e); - } catch (IllegalStateException e) { - pw.println("Failed writing: " + e); - } catch (IOException e) { - pw.println("Failed writing: " + e); - } - } - - if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) { - android.util.IndentingPrintWriter writer = - new android.util.IndentingPrintWriter(pw); - if (dumpState.onTitlePrinted()) pw.println(); + if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) { + dump(DumpState.DUMP_PREFERRED_XML, fd, pw, dumpState); + } - writer.println("Domain verification status:"); - writer.increaseIndent(); - try { - mDomainVerificationManager.printState(writer, packageName, UserHandle.USER_ALL, - mSettings::getPackageLPr); - } catch (PackageManager.NameNotFoundException e) { - pw.println("Failure printing domain verification information"); - Slog.e(TAG, "Failure printing domain verification information", e); - } - writer.decreaseIndent(); - } + if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) { + dump(DumpState.DUMP_DOMAIN_PREFERRED, fd, pw, dumpState); + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) { - mSettings.dumpPermissionsLPr(pw, packageName, permissionNames, dumpState); - } + if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) { + mSettings.dumpPermissions(pw, packageName, permissionNames, dumpState); + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) { + if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) { + synchronized (mLock) { mComponentResolver.dumpContentProviders(pw, dumpState, packageName); } + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_KEYSETS)) { + if (!checkin && dumpState.isDumping(DumpState.DUMP_KEYSETS)) { + synchronized (mLock) { mSettings.getKeySetManagerService().dumpLPr(pw, packageName, dumpState); } + } - if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) { + if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) { + // This cannot be moved to ComputerEngine since some variables of the collections + // in PackageUserState such as suspendParams, disabledComponents and enabledComponents + // do not have a copy. + synchronized (mLock) { mSettings.dumpPackagesLPr(pw, packageName, permissionNames, dumpState, checkin); } + } - if (dumpState.isDumping(DumpState.DUMP_QUERIES)) { - final PackageSetting setting = mSettings.getPackageLPr(packageName); - Integer filteringAppId = setting == null ? null : setting.appId; - mAppsFilter.dumpQueries( - pw, this, filteringAppId, dumpState, - mUserManager.getUserIds()); - } + if (dumpState.isDumping(DumpState.DUMP_QUERIES)) { + dump(DumpState.DUMP_QUERIES, fd, pw, dumpState); + } - if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) { + if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) { + // This cannot be moved to ComputerEngine since the set of packages in the + // SharedUserSetting do not have a copy. + synchronized (mLock) { mSettings.dumpSharedUsersLPr(pw, packageName, permissionNames, dumpState, checkin); } + } - if (dumpState.isDumping(DumpState.DUMP_CHANGES)) { - if (dumpState.onTitlePrinted()) pw.println(); - pw.println("Package Changes:"); + if (dumpState.isDumping(DumpState.DUMP_CHANGES)) { + if (dumpState.onTitlePrinted()) pw.println(); + pw.println("Package Changes:"); + synchronized (mLock) { pw.print(" Sequence number="); pw.println(mChangedPackagesSequenceNumber); final int K = mChangedPackages.size(); for (int i = 0; i < K; i++) { @@ -24032,16 +24126,18 @@ public class PackageManagerService extends IPackageManager.Stub } } } + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_FROZEN) && packageName == null) { - // XXX should handle packageName != null by dumping only install data that - // the given package is involved with. - if (dumpState.onTitlePrinted()) pw.println(); + if (!checkin && dumpState.isDumping(DumpState.DUMP_FROZEN) && packageName == null) { + // XXX should handle packageName != null by dumping only install data that + // the given package is involved with. + if (dumpState.onTitlePrinted()) pw.println(); - final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); - ipw.println(); - ipw.println("Frozen packages:"); - ipw.increaseIndent(); + final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); + ipw.println(); + ipw.println("Frozen packages:"); + ipw.increaseIndent(); + synchronized (mLock) { if (mFrozenPackages.size() == 0) { ipw.println("(none)"); } else { @@ -24049,16 +24145,18 @@ public class PackageManagerService extends IPackageManager.Stub ipw.println(mFrozenPackages.valueAt(i)); } } - ipw.decreaseIndent(); } + ipw.decreaseIndent(); + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_VOLUMES) && packageName == null) { - if (dumpState.onTitlePrinted()) pw.println(); + if (!checkin && dumpState.isDumping(DumpState.DUMP_VOLUMES) && packageName == null) { + if (dumpState.onTitlePrinted()) pw.println(); - final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); - ipw.println(); - ipw.println("Loaded volumes:"); - ipw.increaseIndent(); + final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); + ipw.println(); + ipw.println("Loaded volumes:"); + ipw.increaseIndent(); + synchronized (mLoadedVolumes) { if (mLoadedVolumes.size() == 0) { ipw.println("(none)"); } else { @@ -24066,36 +24164,39 @@ public class PackageManagerService extends IPackageManager.Stub ipw.println(mLoadedVolumes.valueAt(i)); } } - ipw.decreaseIndent(); } + ipw.decreaseIndent(); + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS) - && packageName == null) { + if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS) + && packageName == null) { + synchronized (mLock) { mComponentResolver.dumpServicePermissions(pw, dumpState); } + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_DEXOPT)) { - if (dumpState.onTitlePrinted()) pw.println(); - dumpDexoptStateLPr(pw, packageName); - } + if (!checkin && dumpState.isDumping(DumpState.DUMP_DEXOPT)) { + if (dumpState.onTitlePrinted()) pw.println(); + dump(DumpState.DUMP_DEXOPT, fd, pw, dumpState); + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) { - if (dumpState.onTitlePrinted()) pw.println(); - dumpCompilerStatsLPr(pw, packageName); - } + if (!checkin && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) { + if (dumpState.onTitlePrinted()) pw.println(); + dump(DumpState.DUMP_COMPILER_STATS, fd, pw, dumpState); + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) { - if (dumpState.onTitlePrinted()) pw.println(); + if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) { + if (dumpState.onTitlePrinted()) pw.println(); + synchronized (mLock) { mSettings.dumpReadMessagesLPr(pw, dumpState); - - pw.println(); - pw.println("Package warning messages:"); - dumpCriticalInfo(pw, null); } + pw.println(); + pw.println("Package warning messages:"); + dumpCriticalInfo(pw, null); + } - if (checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES)) { - dumpCriticalInfo(pw, "msg,"); - } + if (checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES)) { + dumpCriticalInfo(pw, "msg,"); } // PackageInstaller should be called outside of mPackages lock @@ -24130,6 +24231,14 @@ public class PackageManagerService extends IPackageManager.Stub } } + /** + * Dump package manager states to the file according to a given dumping type of + * {@link DumpState}. + */ + private void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) { + snapshotComputer().dump(type, fd, pw, dumpState); + } + //TODO: b/111402650 private void disableSkuSpecificApps() { String apkList[] = mContext.getResources().getStringArray( @@ -24228,69 +24337,50 @@ public class PackageManagerService extends IPackageManager.Stub } } - @GuardedBy("mLock") - @SuppressWarnings("resource") - private void dumpDexoptStateLPr(PrintWriter pw, String packageName) { - final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); - ipw.println(); - ipw.println("Dexopt state:"); - ipw.increaseIndent(); - Collection<PackageSetting> pkgSettings; - if (packageName != null) { - PackageSetting targetPkgSetting = mSettings.getPackageLPr(packageName); - if (targetPkgSetting != null) { - pkgSettings = Collections.singletonList(targetPkgSetting); - } else { - ipw.println("Unable to find package: " + packageName); - return; - } - } else { - pkgSettings = mSettings.getPackagesLocked().values(); - } - - for (PackageSetting pkgSetting : pkgSettings) { - if (pkgSetting.pkg == null) { + private void dumpSharedLibrariesLPr(PrintWriter pw, DumpState dumpState, boolean checkin) { + boolean printedHeader = false; + final int numSharedLibraries = mSharedLibraries.size(); + for (int index = 0; index < numSharedLibraries; index++) { + final String libName = mSharedLibraries.keyAt(index); + WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(libName); + if (versionedLib == null) { continue; } - ipw.println("[" + pkgSetting.name + "]"); - ipw.increaseIndent(); - mPackageDexOptimizer.dumpDexoptState(ipw, pkgSetting.pkg, pkgSetting, - mDexManager.getPackageUseInfoOrDefault(pkgSetting.pkg.getPackageName())); - ipw.decreaseIndent(); - } - } - - @GuardedBy("mLock") - @SuppressWarnings("resource") - private void dumpCompilerStatsLPr(PrintWriter pw, String packageName) { - final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); - ipw.println(); - ipw.println("Compiler stats:"); - ipw.increaseIndent(); - Collection<AndroidPackage> packages; - if (packageName != null) { - AndroidPackage targetPackage = mPackages.get(packageName); - if (targetPackage != null) { - packages = Collections.singletonList(targetPackage); - } else { - ipw.println("Unable to find package: " + packageName); - return; - } - } else { - packages = mPackages.values(); - } - - for (AndroidPackage pkg : packages) { - ipw.println("[" + pkg.getPackageName() + "]"); - ipw.increaseIndent(); - - CompilerStats.PackageStats stats = getCompilerPackageStats(pkg.getPackageName()); - if (stats == null) { - ipw.println("(No recorded stats)"); - } else { - stats.dump(ipw); + final int versionCount = versionedLib.size(); + for (int i = 0; i < versionCount; i++) { + SharedLibraryInfo libraryInfo = versionedLib.valueAt(i); + if (!checkin) { + if (!printedHeader) { + if (dumpState.onTitlePrinted()) { + pw.println(); + } + pw.println("Libraries:"); + printedHeader = true; + } + pw.print(" "); + } else { + pw.print("lib,"); + } + pw.print(libraryInfo.getName()); + if (libraryInfo.isStatic()) { + pw.print(" version=" + libraryInfo.getLongVersion()); + } + if (!checkin) { + pw.print(" -> "); + } + if (libraryInfo.getPath() != null) { + if (libraryInfo.isNative()) { + pw.print(" (so) "); + } else { + pw.print(" (jar) "); + } + pw.print(libraryInfo.getPath()); + } else { + pw.print(" (apk) "); + pw.print(libraryInfo.getPackageName()); + } + pw.println(); } - ipw.decreaseIndent(); } } @@ -24452,7 +24542,9 @@ public class PackageManagerService extends IPackageManager.Stub if (DEBUG_INSTALL) Slog.d(TAG, "Loaded packages " + loaded); sendResourcesChangedBroadcast(true, false, loaded, null); - mLoadedVolumes.add(vol.getId()); + synchronized (mLoadedVolumes) { + mLoadedVolumes.add(vol.getId()); + } } private void unloadPrivatePackages(final VolumeInfo vol) { @@ -24500,7 +24592,9 @@ public class PackageManagerService extends IPackageManager.Stub if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded); sendResourcesChangedBroadcast(false, false, unloaded, null); - mLoadedVolumes.remove(vol.getId()); + synchronized (mLoadedVolumes) { + mLoadedVolumes.remove(vol.getId()); + } // Try very hard to release any references to this path so we don't risk // the system server being killed due to open FDs @@ -27432,43 +27526,14 @@ public class PackageManagerService extends IPackageManager.Stub } } - private String getOatDir(AndroidPackage pkg, @NonNull PackageSetting pkgSetting) { - if (!AndroidPackageUtils.canHaveOatDir(pkg, - pkgSetting.getPkgState().isUpdatedSystemApp())) { - return null; - } - File codePath = new File(pkg.getPath()); - if (codePath.isDirectory()) { - return PackageDexOptimizer.getOatDir(codePath).getAbsolutePath(); - } - return null; - } - void deleteOatArtifactsOfPackage(String packageName) { - final String[] instructionSets; - final List<String> codePaths; - final String oatDir; final AndroidPackage pkg; final PackageSetting pkgSetting; synchronized (mLock) { pkg = mPackages.get(packageName); pkgSetting = mSettings.getPackageLPr(packageName); } - instructionSets = getAppDexInstructionSets( - AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting), - AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting)); - codePaths = AndroidPackageUtils.getAllCodePaths(pkg); - oatDir = getOatDir(pkg, pkgSetting); - - for (String codePath : codePaths) { - for (String isa : instructionSets) { - try { - mInstaller.deleteOdex(codePath, isa, oatDir); - } catch (InstallerException e) { - Log.e(TAG, "Failed deleting oat files for " + codePath, e); - } - } - } + mDexManager.deleteOptimizedFiles(ArtUtils.createArtPackageInfo(pkg, pkgSetting)); } Set<String> getUnusedPackages(long downgradeTimeThresholdMillis) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 2112247650a5..ec7b451c6ec9 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -40,7 +40,6 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; import android.content.pm.IntentFilterVerificationInfo; -import android.content.pm.overlay.OverlayPaths; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageUserState; @@ -72,6 +71,7 @@ import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; +import android.os.incremental.IncrementalManager; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; import android.service.pm.PackageServiceDumpProto; @@ -1028,6 +1028,9 @@ public final class Settings implements Watchable, Snappable { pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath; } pkgSetting.setPath(codePath); + if (IncrementalManager.isIncrementalPath(codePath.getAbsolutePath())) { + pkgSetting.incrementalStates = new IncrementalStates(); + } } // If what we are scanning is a system (and possibly privileged) package, // then make it so, regardless of whether it was previously installed only @@ -4877,7 +4880,7 @@ public final class Settings implements Watchable, Snappable { } } - void dumpPermissionsLPr(PrintWriter pw, String packageName, ArraySet<String> permissionNames, + void dumpPermissions(PrintWriter pw, String packageName, ArraySet<String> permissionNames, DumpState dumpState) { LegacyPermissionSettings.dumpPermissions(pw, packageName, permissionNames, mPermissionDataProvider.getLegacyPermissions(), diff --git a/services/core/java/com/android/server/pm/dex/ArtPackageInfo.java b/services/core/java/com/android/server/pm/dex/ArtPackageInfo.java new file mode 100644 index 000000000000..50bf916dceb3 --- /dev/null +++ b/services/core/java/com/android/server/pm/dex/ArtPackageInfo.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm.dex; + +import java.util.List; + +/** + * Holds package information relevant to ART use cases. + */ +public class ArtPackageInfo { + private final String mPackageName; + private final List<String> mInstructionSets; + private final List<String> mCodePaths; + // TODO: This should be computed on the fly in PackageDexOptimizer / DexManager, but the + // logic is too complicated to do it in a single re-factoring. + private final String mOatDir; + + public ArtPackageInfo( + String packageName, + List<String> instructionSets, + List<String> codePaths, + String oatDir) { + mPackageName = packageName; + mInstructionSets = instructionSets; + mCodePaths = codePaths; + mOatDir = oatDir; + } + + public String getPackageName() { + return mPackageName; + } + + public List<String> getInstructionSets() { + return mInstructionSets; + } + + public List<String> getCodePaths() { + return mCodePaths; + } + + public String getOatDir() { + return mOatDir; + } +} diff --git a/services/core/java/com/android/server/pm/dex/ArtUtils.java b/services/core/java/com/android/server/pm/dex/ArtUtils.java new file mode 100644 index 000000000000..16d7a9a0afd8 --- /dev/null +++ b/services/core/java/com/android/server/pm/dex/ArtUtils.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm.dex; + +import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; + +import android.annotation.NonNull; + +import com.android.server.pm.PackageDexOptimizer; +import com.android.server.pm.PackageSetting; +import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.pm.parsing.pkg.AndroidPackageUtils; + +import java.io.File; +import java.util.Arrays; + +/** + * Utility class to interface between PM and ART tooling (e.g. DexManager). + */ +public final class ArtUtils { + private ArtUtils() { + } + + /** + * Create the ART-representation of package info. + */ + public static ArtPackageInfo createArtPackageInfo( + AndroidPackage pkg, PackageSetting pkgSetting) { + return new ArtPackageInfo( + pkg.getPackageName(), + Arrays.asList(getAppDexInstructionSets( + AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting), + AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting))), + AndroidPackageUtils.getAllCodePaths(pkg), + getOatDir(pkg, pkgSetting)); + } + + private static String getOatDir(AndroidPackage pkg, @NonNull PackageSetting pkgSetting) { + if (!AndroidPackageUtils.canHaveOatDir(pkg, + pkgSetting.getPkgState().isUpdatedSystemApp())) { + return null; + } + File codePath = new File(pkg.getPath()); + if (codePath.isDirectory()) { + return PackageDexOptimizer.getOatDir(codePath).getAbsolutePath(); + } + return null; + } + +} diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index cc6d80a2aeec..349561d3f1d1 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -215,7 +215,7 @@ public class DexManager { searchResult.mOutcome == DEX_SEARCH_FOUND_SPLIT; if (primaryOrSplit && !isUsedByOtherApps - && !PLATFORM_PACKAGE_NAME.equals(searchResult.mOwningPackageName)) { + && !isPlatformPackage(searchResult.mOwningPackageName)) { // If the dex file is the primary apk (or a split) and not isUsedByOtherApps // do not record it. This case does not bring any new usable information // and can be safely skipped. @@ -232,15 +232,24 @@ public class DexManager { } String classLoaderContext = mapping.getValue(); + + // Overwrite the class loader context for system server (instead of merging it). + // We expect system server jars to only change contexts in between OTAs and to + // otherwise be stable. + // Instead of implementing a complex clear-context logic post OTA, it is much + // simpler to always override the context for system server. This way, the context + // will always be up to date and we will avoid merging which could lead to the + // the context being marked as variable and thus making dexopt non-optimal. + boolean overwriteCLC = isPlatformPackage(searchResult.mOwningPackageName); + if (classLoaderContext != null && VMRuntime.isValidClassLoaderContext(classLoaderContext)) { // Record dex file usage. If the current usage is a new pattern (e.g. new // secondary, or UsedByOtherApps), record will return true and we trigger an // async write to disk to make sure we don't loose the data in case of a reboot. - if (mPackageDexUsage.record(searchResult.mOwningPackageName, dexPath, loaderUserId, loaderIsa, primaryOrSplit, - loadingAppInfo.packageName, classLoaderContext)) { + loadingAppInfo.packageName, classLoaderContext, overwriteCLC)) { mPackageDexUsage.maybeWriteAsync(); } } @@ -474,7 +483,7 @@ public class DexManager { * because they don't need to be compiled).. */ public boolean dexoptSecondaryDex(DexoptOptions options) { - if (PLATFORM_PACKAGE_NAME.equals(options.getPackageName())) { + if (isPlatformPackage(options.getPackageName())) { // We could easily redirect to #dexoptSystemServer in this case. But there should be // no-one calling this method directly for system server. // As such we prefer to abort in this case. @@ -534,7 +543,7 @@ public class DexManager { * <p>PackageDexOptimizer.DEX_OPT_PERFORMED if all dexopt operations succeeded. */ public int dexoptSystemServer(DexoptOptions options) { - if (!PLATFORM_PACKAGE_NAME.equals(options.getPackageName())) { + if (!isPlatformPackage(options.getPackageName())) { Slog.wtf(TAG, "Non system server package used when trying to dexopt system server:" + options.getPackageName()); return PackageDexOptimizer.DEX_OPT_FAILED; @@ -662,7 +671,7 @@ public class DexManager { // Special handle system server files. // We don't need an installd call because we have permissions to check if the file // exists. - if (PLATFORM_PACKAGE_NAME.equals(packageName)) { + if (isPlatformPackage(packageName)) { if (!Files.exists(Paths.get(dexPath))) { if (DEBUG) { Slog.w(TAG, "A dex file previously loaded by System Server does not exist " @@ -739,7 +748,8 @@ public class DexManager { boolean newUpdate = mPackageDexUsage.record(searchResult.mOwningPackageName, dexPath, userId, isa, /*primaryOrSplit*/ false, loadingPackage, - PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT); + PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT, + /*overwriteCLC*/ false); update |= newUpdate; } if (update) { @@ -809,7 +819,7 @@ public class DexManager { // Note: We don't have any way to detect which code paths are actually // owned by system server. We can only assume that such paths are on // system partitions. - if (PLATFORM_PACKAGE_NAME.equals(loadingAppInfo.packageName)) { + if (isPlatformPackage(loadingAppInfo.packageName)) { if (isSystemServerDexPathSupportedForOdex(dexPath)) { // We record system server dex files as secondary dex files. // The reason is that we only record the class loader context for secondary dex @@ -842,6 +852,11 @@ public class DexManager { return new DexSearchResult(null, DEX_SEARCH_NOT_FOUND); } + /** Returns true if this is the platform package .*/ + private static boolean isPlatformPackage(String packageName) { + return PLATFORM_PACKAGE_NAME.equals(packageName); + } + private static <K,V> V putIfAbsent(Map<K,V> map, K key, V newValue) { V existingValue = map.putIfAbsent(key, newValue); return existingValue == null ? newValue : existingValue; @@ -1000,6 +1015,22 @@ public class DexManager { return isBtmCritical; } + /** + * Deletes all the optimizations files generated by ART. + * @param packageInfo the package information. + */ + public void deleteOptimizedFiles(ArtPackageInfo packageInfo) { + for (String codePath : packageInfo.getCodePaths()) { + for (String isa : packageInfo.getInstructionSets()) { + try { + mInstaller.deleteOdex(codePath, isa, packageInfo.getOatDir()); + } catch (InstallerException e) { + Log.e(TAG, "Failed deleting oat files for " + codePath, e); + } + } + } + } + public static class RegisterDexModuleResult { public RegisterDexModuleResult() { this(false, null); diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java index 10760f52a02b..3d63b75c5da1 100644 --- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java +++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java @@ -111,17 +111,18 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { * @param dexPath the path of the dex files being loaded * @param ownerUserId the user id which runs the code loading the dex files * @param loaderIsa the ISA of the app loading the dex files - * @param isUsedByOtherApps whether or not this dex file was not loaded by its owning package * @param primaryOrSplit whether or not the dex file is a primary/split dex. True indicates * the file is either primary or a split. False indicates the file is secondary dex. * @param loadingPackageName the package performing the load. Recorded only if it is different * than {@param owningPackageName}. + * @param overwriteCLC if true, the class loader context will be overwritten instead of being + * merged * @return true if the dex load constitutes new information, or false if this information * has been seen before. */ /* package */ boolean record(String owningPackageName, String dexPath, int ownerUserId, String loaderIsa, boolean primaryOrSplit, - String loadingPackageName, String classLoaderContext) { + String loadingPackageName, String classLoaderContext, boolean overwriteCLC) { if (!PackageManagerServiceUtils.checkISA(loaderIsa)) { throw new IllegalArgumentException("loaderIsa " + loaderIsa + " is unsupported"); } @@ -193,7 +194,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { } // Merge the information into the existing data. // Returns true if there was an update. - return existingData.merge(newData) || updateLoadingPackages; + return existingData.merge(newData, overwriteCLC) || updateLoadingPackages; } } } @@ -809,14 +810,16 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { mLoadingPackages = new HashSet<>(other.mLoadingPackages); } - private boolean merge(DexUseInfo dexUseInfo) { + private boolean merge(DexUseInfo dexUseInfo, boolean overwriteCLC) { boolean oldIsUsedByOtherApps = mIsUsedByOtherApps; mIsUsedByOtherApps = mIsUsedByOtherApps || dexUseInfo.mIsUsedByOtherApps; boolean updateIsas = mLoaderIsas.addAll(dexUseInfo.mLoaderIsas); boolean updateLoadingPackages = mLoadingPackages.addAll(dexUseInfo.mLoadingPackages); String oldClassLoaderContext = mClassLoaderContext; - if (isUnsupportedContext(mClassLoaderContext)) { + if (overwriteCLC) { + mClassLoaderContext = dexUseInfo.mClassLoaderContext; + } else if (isUnsupportedContext(mClassLoaderContext)) { mClassLoaderContext = dexUseInfo.mClassLoaderContext; } else if (!Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) { // We detected a context change. diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 71e53d9f1f40..7a936ec29498 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -68,15 +68,10 @@ import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.admin.DevicePolicyManagerInternal; -import android.app.role.RoleManager; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.PermissionGroupInfoFlags; import android.content.pm.PackageManager.PermissionInfoFlags; @@ -86,7 +81,6 @@ import android.content.pm.PackageParser; import android.content.pm.ParceledListSlice; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; -import android.content.pm.UserInfo; import android.content.pm.parsing.component.ParsedPermission; import android.content.pm.parsing.component.ParsedPermissionGroup; import android.content.pm.permission.SplitPermissionInfoParcelable; @@ -401,105 +395,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { new PermissionManagerServiceInternalImpl(); LocalServices.addService(PermissionManagerServiceInternal.class, localService); LocalServices.addService(PermissionManagerInternal.class, localService); - - context.getMainThreadHandler().post(() -> context.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (!Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { - return; - } - - try { - fixBgMicCamera(context); - } catch (Throwable t) { - // Don't crash the system if this fails for any reason. Any intermediate state - // this can leave the permissions in is okay and in the worst case the state is - // the same as before the user rebooted. - Log.e(LOG_TAG, "Unable to fix background permissions", t); - } - } - - - private void fixBgMicCamera(Context context) { - PackageManager pm = context.getPackageManager(); - for (UserInfo userInfo : context.getSystemService(UserManager.class).getUsers()) { - UserHandle user = userInfo.getUserHandle(); - List<String> assistants = context.getSystemService(RoleManager.class) - .getRoleHoldersAsUser(RoleManager.ROLE_ASSISTANT, user); - List<PackageInfo> packages = - pm.getInstalledPackagesAsUser(PackageManager.MATCH_SYSTEM_ONLY - | PackageManager.GET_PERMISSIONS, user.getIdentifier()); - for (PackageInfo packageInfo : packages) { - String[] requestedPermissions = packageInfo.requestedPermissions; - if (requestedPermissions == null) { - continue; - } - for (String permName : requestedPermissions) { - String pkg = packageInfo.packageName; - switch (permName) { - case Manifest.permission.BACKGROUND_CAMERA: - removeFromAllowlistsAndRevoke(pm, pkg, permName, user); - break; - case Manifest.permission.RECORD_BACKGROUND_AUDIO: - if (assistants.contains(pkg)) { - removeFromAllowlistsAndRevokeForAssistant(pm, pkg, permName, - user); - } else { - removeFromAllowlistsAndRevoke(pm, pkg, permName, user); - } - break; - } - } - } - } - } - - private void removeFromAllowlistsAndRevoke(PackageManager pm, String pkg, - String permName, UserHandle user) { - if ((pm.getPermissionFlags(permName, pkg, user) - & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0) { - Slog.i(LOG_TAG, "removing " + pkg + " " + permName + " from all allowlists"); - pm.removeWhitelistedRestrictedPermission(pkg, permName, - FLAG_PERMISSION_WHITELIST_UPGRADE); - pm.removeWhitelistedRestrictedPermission(pkg, permName, - FLAG_PERMISSION_WHITELIST_SYSTEM); - pm.removeWhitelistedRestrictedPermission(pkg, permName, - FLAG_PERMISSION_WHITELIST_INSTALLER); - pm.removeWhitelistedRestrictedPermission(pkg, permName, - FLAG_PERMISSION_ALLOWLIST_ROLE); - } - if (pm.checkPermission(permName, pkg) == PackageManager.PERMISSION_GRANTED) { - Slog.i(LOG_TAG, "revoking " + pkg + " " + permName); - pm.revokeRuntimePermission(pkg, permName, user); - } - } - - private void removeFromAllowlistsAndRevokeForAssistant(PackageManager pm, String pkg, - String permName, UserHandle user) { - int anyNonRoleExempt = - FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT - | FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT - | FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; - - if ((pm.getPermissionFlags(permName, pkg, user) & anyNonRoleExempt) != 0) { - Slog.i(LOG_TAG, "removing " + pkg + " " + permName - + " from all allowlists except role"); - pm.removeWhitelistedRestrictedPermission(pkg, permName, - FLAG_PERMISSION_WHITELIST_UPGRADE); - pm.removeWhitelistedRestrictedPermission(pkg, permName, - FLAG_PERMISSION_WHITELIST_SYSTEM); - pm.removeWhitelistedRestrictedPermission(pkg, permName, - FLAG_PERMISSION_WHITELIST_INSTALLER); - } - if ((pm.getPermissionFlags(permName, pkg, user) - & FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT) == 0) { - Slog.i(LOG_TAG, "adding " + pkg + " " + permName - + " to role allowlist"); - pm.addWhitelistedRestrictedPermission(pkg, permName, - FLAG_PERMISSION_ALLOWLIST_ROLE); - } - } - }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED))); } @Override diff --git a/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java b/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java new file mode 100644 index 000000000000..2fcd178c3d15 --- /dev/null +++ b/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java @@ -0,0 +1,280 @@ +/* + * 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.power; + +import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP; +import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE; +import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING; +import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING; + +import static com.android.server.power.DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener.DISPLAY_GROUP_ADDED; +import static com.android.server.power.DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener.DISPLAY_GROUP_CHANGED; +import static com.android.server.power.DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener.DISPLAY_GROUP_REMOVED; + +import android.hardware.display.DisplayManagerInternal; +import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; +import android.os.PowerManagerInternal; +import android.util.Slog; +import android.util.SparseArray; +import android.view.Display; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.ArrayUtils; +import com.android.server.display.DisplayGroup; + +/** + * Responsible for creating {@link DisplayPowerRequest}s and associating them with + * {@link com.android.server.display.DisplayGroup}s. + * + * Each {@link com.android.server.display.DisplayGroup} has a single {@link DisplayPowerRequest} + * which is used to request power state changes to every display in the group. + */ +public class DisplayGroupPowerStateMapper { + + private static final String TAG = "DisplayPowerRequestMapper"; + + /** Lock obtained from {@link PowerManagerService}. */ + private final Object mLock; + + /** Listener to inform of changes to display groups. */ + private final DisplayGroupPowerChangeListener mListener; + + /** A mapping from DisplayGroup Id to DisplayGroup information. */ + @GuardedBy("mLock") + private final SparseArray<DisplayGroupInfo> mDisplayGroupInfos = new SparseArray<>(); + + /** A cached array of DisplayGroup Ids. */ + @GuardedBy("mLock") + private int[] mDisplayGroupIds; + + private final DisplayManagerInternal.DisplayGroupListener mDisplayGroupListener = + new DisplayManagerInternal.DisplayGroupListener() { + @Override + public void onDisplayGroupAdded(int groupId) { + synchronized (mLock) { + if (mDisplayGroupInfos.contains(groupId)) { + Slog.e(TAG, "Tried to add already existing group:" + groupId); + return; + } + // For now, only the default group supports sandman (dream/AOD). + final boolean supportsSandman = groupId == Display.DEFAULT_DISPLAY_GROUP; + final DisplayGroupInfo displayGroupInfo = new DisplayGroupInfo( + new DisplayPowerRequest(), + getGlobalWakefulnessLocked(), + /* ready= */ false, + supportsSandman); + mDisplayGroupInfos.append(groupId, displayGroupInfo); + mDisplayGroupIds = ArrayUtils.appendInt(mDisplayGroupIds, groupId); + mListener.onDisplayGroupEventLocked(DISPLAY_GROUP_ADDED, groupId); + } + } + + @Override + public void onDisplayGroupRemoved(int groupId) { + synchronized (mLock) { + if (!mDisplayGroupInfos.contains(groupId)) { + Slog.e(TAG, "Tried to remove non-existent group:" + groupId); + return; + } + mDisplayGroupInfos.delete(groupId); + mDisplayGroupIds = ArrayUtils.removeInt(mDisplayGroupIds, groupId); + mListener.onDisplayGroupEventLocked(DISPLAY_GROUP_REMOVED, groupId); + } + } + + @Override + public void onDisplayGroupChanged(int groupId) { + synchronized (mLock) { + mListener.onDisplayGroupEventLocked(DISPLAY_GROUP_CHANGED, groupId); + } + } + }; + + DisplayGroupPowerStateMapper(Object lock, DisplayManagerInternal displayManagerInternal, + DisplayGroupPowerChangeListener listener) { + mLock = lock; + mListener = listener; + displayManagerInternal.registerDisplayGroupListener(mDisplayGroupListener); + + final DisplayGroupInfo displayGroupInfo = new DisplayGroupInfo( + new DisplayPowerRequest(), WAKEFULNESS_AWAKE, /* ready= */ + false, /* supportsSandman= */ true); + mDisplayGroupInfos.append(Display.DEFAULT_DISPLAY_GROUP, displayGroupInfo); + mDisplayGroupIds = new int[]{Display.DEFAULT_DISPLAY_GROUP}; + } + + DisplayPowerRequest getPowerRequestLocked(int groupId) { + return mDisplayGroupInfos.get(groupId).displayPowerRequest; + } + + int[] getDisplayGroupIdsLocked() { + return mDisplayGroupIds; + } + + int getWakefulnessLocked(int groupId) { + return mDisplayGroupInfos.get(groupId).wakefulness; + } + + void setLastPowerOnTimeLocked(int groupId, long eventTime) { + mDisplayGroupInfos.get(groupId).lastPowerOnTime = eventTime; + } + + long getLastPowerOnTimeLocked(int groupId) { + return mDisplayGroupInfos.get(groupId).lastPowerOnTime; + } + + /** + * Returns the amalgamated wakefulness of all {@link DisplayGroup DisplayGroups}. + * + * <p>This will be the highest wakeful state of all {@link DisplayGroup DisplayGroups}; ordered + * from highest to lowest: + * <ol> + * <li>{@link PowerManagerInternal#WAKEFULNESS_AWAKE} + * <li>{@link PowerManagerInternal#WAKEFULNESS_DREAMING} + * <li>{@link PowerManagerInternal#WAKEFULNESS_DOZING} + * <li>{@link PowerManagerInternal#WAKEFULNESS_ASLEEP} + * </ol> + */ + int getGlobalWakefulnessLocked() { + final int size = mDisplayGroupInfos.size(); + int deviceWakefulness = WAKEFULNESS_ASLEEP; + for (int i = 0; i < size; i++) { + final int wakefulness = mDisplayGroupInfos.valueAt(i).wakefulness; + if (wakefulness == WAKEFULNESS_AWAKE) { + return WAKEFULNESS_AWAKE; + } else if (wakefulness == WAKEFULNESS_DREAMING + && (deviceWakefulness == WAKEFULNESS_ASLEEP + || deviceWakefulness == WAKEFULNESS_DOZING)) { + deviceWakefulness = WAKEFULNESS_DREAMING; + } else if (wakefulness == WAKEFULNESS_DOZING + && deviceWakefulness == WAKEFULNESS_ASLEEP) { + deviceWakefulness = WAKEFULNESS_DOZING; + } + } + + return deviceWakefulness; + } + + /** + * Sets the {@code wakefulness} value for the {@link DisplayGroup} specified by the provided + * {@code groupId}. + * + * @return {@code true} if the wakefulness value was changed; {@code false} otherwise. + */ + boolean setWakefulnessLocked(int groupId, int wakefulness) { + final DisplayGroupInfo displayGroupInfo = mDisplayGroupInfos.get(groupId); + if (displayGroupInfo.wakefulness != wakefulness) { + displayGroupInfo.wakefulness = wakefulness; + return true; + } + + return false; + } + + boolean isSandmanSummoned(int groupId) { + return mDisplayGroupInfos.get(groupId).sandmanSummoned; + } + + boolean isSandmanSupported(int groupId) { + return mDisplayGroupInfos.get(groupId).supportsSandman; + } + + /** + * Sets whether or not the sandman is summoned for the given {@code groupId}. + * + * @param groupId Signifies the DisplayGroup for which to summon or unsummon the + * sandman. + * @param sandmanSummoned {@code true} to summon the sandman; {@code false} to unsummon. + */ + void setSandmanSummoned(int groupId, boolean sandmanSummoned) { + final DisplayGroupInfo displayGroupInfo = mDisplayGroupInfos.get(groupId); + displayGroupInfo.sandmanSummoned = displayGroupInfo.supportsSandman && sandmanSummoned; + } + + /** + * Returns {@code true} if every display in the specified group has its requested state matching + * its actual state. + * + * @param groupId The identifier for the display group to check for readiness. + */ + boolean isReady(int groupId) { + return mDisplayGroupInfos.get(groupId).ready; + } + + /** Returns {@code true} if every display has its requested state matching its actual state. */ + boolean areAllDisplaysReadyLocked() { + final int size = mDisplayGroupInfos.size(); + for (int i = 0; i < size; i++) { + if (!mDisplayGroupInfos.valueAt(i).ready) { + return false; + } + } + + return true; + } + + /** + * Sets whether the displays specified by the provided {@code groupId} are all ready. + * + * <p>A display is ready if its reported + * {@link DisplayManagerInternal.DisplayPowerCallbacks#onStateChanged() actual state} matches + * its {@link DisplayManagerInternal#requestPowerState requested state}. + * + * @param groupId The identifier for the display group. + * @param ready {@code true} if every display in the group is ready; otherwise {@code false}. + * @return {@code true} if the ready state changed; otherwise {@code false}. + */ + boolean setDisplayGroupReadyLocked(int groupId, boolean ready) { + final DisplayGroupInfo displayGroupInfo = mDisplayGroupInfos.get(groupId); + if (displayGroupInfo.ready != ready) { + displayGroupInfo.ready = ready; + return true; + } + + return false; + } + + /** + * Interface through which an interested party may be informed of {@link DisplayGroup} events. + */ + interface DisplayGroupPowerChangeListener { + int DISPLAY_GROUP_ADDED = 0; + int DISPLAY_GROUP_REMOVED = 1; + int DISPLAY_GROUP_CHANGED = 2; + + void onDisplayGroupEventLocked(int event, int groupId); + } + + private static final class DisplayGroupInfo { + public final DisplayPowerRequest displayPowerRequest; + public int wakefulness; + public boolean ready; + public long lastPowerOnTime; + public boolean sandmanSummoned; + + /** {@code true} if this DisplayGroup supports dreaming; otherwise {@code false}. */ + public boolean supportsSandman; + + DisplayGroupInfo(DisplayPowerRequest displayPowerRequest, int wakefulness, boolean ready, + boolean supportsSandman) { + this.displayPowerRequest = displayPowerRequest; + this.wakefulness = wakefulness; + this.ready = ready; + this.supportsSandman = supportsSandman; + } + } +} diff --git a/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java b/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java deleted file mode 100644 index 2fc3e40acd4d..000000000000 --- a/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.power; - -import android.hardware.display.DisplayManager; -import android.hardware.display.DisplayManagerInternal; -import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; -import android.os.Handler; -import android.util.SparseArray; -import android.util.SparseIntArray; -import android.view.Display; - -import com.android.internal.annotations.GuardedBy; - -/** - * Responsible for creating {@link DisplayPowerRequest}s and associating them with - * {@link com.android.server.display.DisplayGroup}s. - * - * Each {@link com.android.server.display.DisplayGroup} has a single {@link DisplayPowerRequest} - * which is used to request power state changes to every display in the group. - */ -class DisplayPowerRequestMapper { - - private final Object mLock = new Object(); - - /** A mapping from LogicalDisplay Id to DisplayGroup Id. */ - @GuardedBy("mLock") - private final SparseIntArray mDisplayGroupIds = new SparseIntArray(); - - /** A mapping from DisplayGroup Id to DisplayPowerRequest. */ - @GuardedBy("mLock") - private final SparseArray<DisplayPowerRequest> mDisplayPowerRequests = new SparseArray<>(); - - private final DisplayManagerInternal mDisplayManagerInternal; - - private final DisplayManager.DisplayListener mDisplayListener = - new DisplayManager.DisplayListener() { - - @Override - public void onDisplayAdded(int displayId) { - synchronized (mLock) { - if (mDisplayGroupIds.indexOfKey(displayId) >= 0) { - return; - } - final int displayGroupId = mDisplayManagerInternal.getDisplayGroupId( - displayId); - if (!mDisplayPowerRequests.contains(displayGroupId)) { - // A new DisplayGroup was created; create a new DisplayPowerRequest. - mDisplayPowerRequests.append(displayGroupId, new DisplayPowerRequest()); - } - mDisplayGroupIds.append(displayId, displayGroupId); - } - } - - @Override - public void onDisplayRemoved(int displayId) { - synchronized (mLock) { - final int index = mDisplayGroupIds.indexOfKey(displayId); - if (index < 0) { - return; - } - final int displayGroupId = mDisplayGroupIds.valueAt(index); - mDisplayGroupIds.removeAt(index); - - if (mDisplayGroupIds.indexOfValue(displayGroupId) < 0) { - // The DisplayGroup no longer exists; delete the DisplayPowerRequest. - mDisplayPowerRequests.delete(displayGroupId); - } - } - } - - @Override - public void onDisplayChanged(int displayId) { - synchronized (mLock) { - final int newDisplayGroupId = mDisplayManagerInternal.getDisplayGroupId( - displayId); - final int oldDisplayGroupId = mDisplayGroupIds.get(displayId); - - if (!mDisplayPowerRequests.contains(newDisplayGroupId)) { - // A new DisplayGroup was created; create a new DisplayPowerRequest. - mDisplayPowerRequests.append(newDisplayGroupId, - new DisplayPowerRequest()); - } - mDisplayGroupIds.put(displayId, newDisplayGroupId); - - if (mDisplayGroupIds.indexOfValue(oldDisplayGroupId) < 0) { - // The DisplayGroup no longer exists; delete the DisplayPowerRequest. - mDisplayPowerRequests.delete(oldDisplayGroupId); - } - } - } - }; - - DisplayPowerRequestMapper(DisplayManager displayManager, - DisplayManagerInternal displayManagerInternal, Handler handler) { - mDisplayManagerInternal = displayManagerInternal; - displayManager.registerDisplayListener(mDisplayListener, handler); - mDisplayPowerRequests.append(Display.DEFAULT_DISPLAY_GROUP, new DisplayPowerRequest()); - mDisplayGroupIds.append(Display.DEFAULT_DISPLAY, Display.DEFAULT_DISPLAY_GROUP); - } - - DisplayPowerRequest get(int displayId) { - synchronized (mLock) { - return mDisplayPowerRequests.get(mDisplayGroupIds.get(displayId)); - } - } -} diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index c07974aff0b9..bc117094dd68 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -16,8 +16,13 @@ package com.android.server.power; +import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.policyToString; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT; +import static android.os.PowerManager.GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF; +import static android.os.PowerManager.GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED; +import static android.os.PowerManager.WAKE_REASON_DISPLAY_GROUP_ADDED; +import static android.os.PowerManager.WAKE_REASON_DISPLAY_GROUP_TURNED_ON; import static android.os.PowerManagerInternal.MODE_DEVICE_IDLE; import static android.os.PowerManagerInternal.MODE_DISPLAY_INACTIVE; import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP; @@ -42,7 +47,6 @@ import android.database.ContentObserver; import android.hardware.SensorManager; import android.hardware.SystemSensorManager; import android.hardware.display.AmbientDisplayConfiguration; -import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; import android.hardware.power.Boost; @@ -178,6 +182,8 @@ public final class PowerManagerService extends SystemService private static final int DIRTY_ATTENTIVE = 1 << 14; // Dirty bit: phone flipped to face down private static final int DIRTY_FACE_DOWN = 1 << 15; + // Dirty bit: display group power state has changed + private static final int DIRTY_DISPLAY_GROUP_POWER_UPDATED = 1 << 16; // Summarizes the state of all active wakelocks. private static final int WAKE_LOCK_CPU = 1 << 0; @@ -300,10 +306,6 @@ public final class PowerManagerService extends SystemService private int mWakefulnessRaw; private boolean mWakefulnessChanging; - // True if the sandman has just been summoned for the first time since entering the - // dreaming or dozing state. Indicates whether a new dream should begin. - private boolean mSandmanSummoned; - // True if MSG_SANDMAN has been scheduled. private boolean mSandmanScheduled; @@ -354,11 +356,7 @@ public final class PowerManagerService extends SystemService // Manages the desired power state of displays. The actual state may lag behind the // requested because it is updated asynchronously by the display power controller. - private DisplayPowerRequestMapper mDisplayPowerRequestMapper; - - // True if the display power state has been fully applied, which means the display - // is actually on or actually off or whatever was requested. - private boolean mDisplayReady; + private DisplayGroupPowerStateMapper mDisplayGroupPowerStateMapper; // The suspend blocker used to keep the CPU alive when an application has acquired // a wake lock. @@ -632,6 +630,39 @@ public final class PowerManagerService extends SystemService // but the DreamService has not yet been told to start (it's an async process). private boolean mDozeStartInProgress; + private final class DisplayGroupPowerChangeListener implements + DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener { + @Override + public void onDisplayGroupEventLocked(int event, int groupId) { + final int oldWakefulness = getWakefulnessLocked(); + final int newWakefulness = mDisplayGroupPowerStateMapper.getGlobalWakefulnessLocked(); + if (oldWakefulness != newWakefulness) { + final int reason; + switch (newWakefulness) { + case WAKEFULNESS_AWAKE: + reason = event == DISPLAY_GROUP_ADDED ? WAKE_REASON_DISPLAY_GROUP_ADDED + : WAKE_REASON_DISPLAY_GROUP_TURNED_ON; + break; + case WAKEFULNESS_DOZING: + reason = event == DISPLAY_GROUP_REMOVED + ? GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED + : GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF; + break; + default: + reason = 0; + } + + setGlobalWakefulnessLocked( + mDisplayGroupPowerStateMapper.getGlobalWakefulnessLocked(), + mClock.uptimeMillis(), reason, Process.SYSTEM_UID, Process.SYSTEM_UID, + mContext.getOpPackageName(), "groupId: " + groupId); + } + + mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED; + updatePowerStateLocked(); + } + } + private final class ForegroundProfileObserver extends SynchronousUserSwitchObserver { @Override public void onUserSwitching(@UserIdInt int newUserId) throws RemoteException { @@ -877,6 +908,12 @@ public final class PowerManagerService extends SystemService void invalidateIsInteractiveCaches() { PowerManager.invalidateIsInteractiveCaches(); } + + DisplayGroupPowerStateMapper createDisplayPowerRequestMapper(Object lock, + DisplayManagerInternal displayManagerInternal, + DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener listener) { + return new DisplayGroupPowerStateMapper(lock, displayManagerInternal, listener); + } } final Constants mConstants; @@ -1068,7 +1105,8 @@ public final class PowerManagerService extends SystemService updatePowerStateLocked(); if (sQuiescent) { - goToSleepNoUpdateLocked(mClock.uptimeMillis(), + sleepDisplayGroupNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, + mClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_QUIESCENT, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID); } @@ -1085,8 +1123,8 @@ public final class PowerManagerService extends SystemService mPolicy = getLocalService(WindowManagerPolicy.class); mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class); mAttentionDetector.systemReady(mContext); - mDisplayPowerRequestMapper = new DisplayPowerRequestMapper(mContext.getSystemService( - DisplayManager.class), mDisplayManagerInternal, mHandler); + mDisplayGroupPowerStateMapper = mInjector.createDisplayPowerRequestMapper(mLock, + mDisplayManagerInternal, new DisplayGroupPowerChangeListener()); SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper()); @@ -1404,9 +1442,11 @@ public final class PowerManagerService extends SystemService opPackageName = wakeLock.mPackageName; opUid = wakeLock.mOwnerUid; } - wakeUpNoUpdateLocked(mClock.uptimeMillis(), - PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag, - opUid, opPackageName, opUid); + for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { + wakeDisplayGroupNoUpdateLocked(id, mClock.uptimeMillis(), + PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag, + opUid, opPackageName, opUid); + } } } @@ -1695,26 +1735,29 @@ public final class PowerManagerService extends SystemService } } - private void wakeUpInternal(long eventTime, @WakeReason int reason, String details, int uid, - String opPackageName, int opUid) { + private void wakeDisplayGroup(int groupId, long eventTime, @WakeReason int reason, + String details, int uid, String opPackageName, int opUid) { synchronized (mLock) { - if (wakeUpNoUpdateLocked(eventTime, reason, details, uid, opPackageName, opUid)) { + if (wakeDisplayGroupNoUpdateLocked(groupId, eventTime, reason, details, uid, + opPackageName, opUid)) { updatePowerStateLocked(); } } } - private boolean wakeUpNoUpdateLocked(long eventTime, @WakeReason int reason, String details, - int reasonUid, String opPackageName, int opUid) { + private boolean wakeDisplayGroupNoUpdateLocked(int groupId, long eventTime, + @WakeReason int reason, String details, int uid, String opPackageName, int opUid) { if (DEBUG_SPEW) { - Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime + ", uid=" + reasonUid); + Slog.d(TAG, "wakeDisplayGroupNoUpdateLocked: eventTime=" + eventTime + + ", groupId=" + groupId + ", uid=" + uid); } if (eventTime < mLastSleepTime || mForceSuspendActive || !mSystemReady) { return false; } - if (getWakefulnessLocked() == WAKEFULNESS_AWAKE) { + final int currentState = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId); + if (currentState == WAKEFULNESS_AWAKE) { if (!mBootCompleted && sQuiescent) { mDirty |= DIRTY_QUIESCENT; return true; @@ -1722,113 +1765,90 @@ public final class PowerManagerService extends SystemService return false; } - Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0); - - Trace.traceBegin(Trace.TRACE_TAG_POWER, "wakeUp"); + Trace.traceBegin(Trace.TRACE_TAG_POWER, "powerOnDisplay"); try { - Slog.i(TAG, "Waking up from " - + PowerManagerInternal.wakefulnessToString(getWakefulnessLocked()) - + " (uid=" + reasonUid + Slog.i(TAG, "Powering on display group from" + + PowerManagerInternal.wakefulnessToString(currentState) + + " (groupId=" + groupId + + ", uid=" + uid + ", reason=" + PowerManager.wakeReasonToString(reason) + ", details=" + details + ")..."); + Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, groupId); - mLastWakeTime = eventTime; - mLastWakeReason = reason; - setWakefulnessLocked(WAKEFULNESS_AWAKE, reason, eventTime); - - mNotifier.onWakeUp(reason, details, reasonUid, opPackageName, opUid); - userActivityNoUpdateLocked( - eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid); - - if (sQuiescent) { - mDirty |= DIRTY_QUIESCENT; - } + setWakefulnessLocked(groupId, WAKEFULNESS_AWAKE, eventTime, uid, reason, opUid, + opPackageName, details); + mDisplayGroupPowerStateMapper.setLastPowerOnTimeLocked(groupId, eventTime); + mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED; } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } + return true; } - private void goToSleepInternal(long eventTime, int reason, int flags, int uid) { + private void sleepDisplayGroup(int groupId, long eventTime, int reason, int flags, + int uid) { synchronized (mLock) { - if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) { + if (sleepDisplayGroupNoUpdateLocked(groupId, eventTime, reason, flags, uid)) { updatePowerStateLocked(); } } } - /** - * Puts the system in doze. - * - * This method is called goToSleep for historical reasons but actually attempts to DOZE, - * and only tucks itself in to SLEEP if requested with the flag - * {@link PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE}. - */ - @SuppressWarnings("deprecation") - private boolean goToSleepNoUpdateLocked(long eventTime, int reason, int flags, int uid) { + private boolean sleepDisplayGroupNoUpdateLocked(int groupId, long eventTime, int reason, + int flags, int uid) { if (DEBUG_SPEW) { - Slog.d(TAG, "goToSleepNoUpdateLocked: eventTime=" + eventTime - + ", reason=" + reason + ", flags=" + flags + ", uid=" + uid); + Slog.d(TAG, "sleepDisplayGroupNoUpdateLocked: eventTime=" + eventTime + + ", groupId=" + groupId + ", reason=" + reason + ", flags=" + flags + + ", uid=" + uid); } if (eventTime < mLastWakeTime - || getWakefulnessLocked() == WAKEFULNESS_ASLEEP - || getWakefulnessLocked() == WAKEFULNESS_DOZING + || !PowerManagerInternal.isInteractive(getWakefulnessLocked()) || !mSystemReady || !mBootCompleted) { return false; } - Trace.traceBegin(Trace.TRACE_TAG_POWER, "goToSleep"); + final int wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId); + if (!PowerManagerInternal.isInteractive(wakefulness)) { + return false; + } + + Trace.traceBegin(Trace.TRACE_TAG_POWER, "powerOffDisplay"); try { reason = Math.min(PowerManager.GO_TO_SLEEP_REASON_MAX, Math.max(reason, PowerManager.GO_TO_SLEEP_REASON_MIN)); - Slog.i(TAG, "Going to sleep due to " + PowerManager.sleepReasonToString(reason) - + " (uid " + uid + ")..."); - - mLastSleepTime = eventTime; - mLastSleepReason = reason; - mSandmanSummoned = true; - mDozeStartInProgress = true; - setWakefulnessLocked(WAKEFULNESS_DOZING, reason, eventTime); - - // Report the number of wake locks that will be cleared by going to sleep. - int numWakeLocksCleared = 0; - final int numWakeLocks = mWakeLocks.size(); - for (int i = 0; i < numWakeLocks; i++) { - final WakeLock wakeLock = mWakeLocks.get(i); - switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) { - case PowerManager.FULL_WAKE_LOCK: - case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: - case PowerManager.SCREEN_DIM_WAKE_LOCK: - numWakeLocksCleared += 1; - break; - } - } - EventLogTags.writePowerSleepRequested(numWakeLocksCleared); + Slog.i(TAG, "Powering off display group due to " + + PowerManager.sleepReasonToString(reason) + " (groupId= " + groupId + + ", uid= " + uid + ")..."); - // Skip dozing if requested. + mDisplayGroupPowerStateMapper.setSandmanSummoned(groupId, true); + setWakefulnessLocked(groupId, WAKEFULNESS_DOZING, eventTime, uid, reason, + /* opUid= */ 0, /* opPackageName= */ null, /* details= */ null); if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) { - reallyGoToSleepNoUpdateLocked(eventTime, uid); + reallySleepDisplayGroupNoUpdateLocked(groupId, eventTime, uid); } + mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED; } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } return true; } - private void napInternal(long eventTime, int uid) { + private void dreamDisplayGroup(int groupId, long eventTime, int uid) { synchronized (mLock) { - if (napNoUpdateLocked(eventTime, uid)) { + if (dreamDisplayGroupNoUpdateLocked(groupId, eventTime, uid)) { updatePowerStateLocked(); } } } - private boolean napNoUpdateLocked(long eventTime, int uid) { + private boolean dreamDisplayGroupNoUpdateLocked(int groupId, long eventTime, int uid) { if (DEBUG_SPEW) { - Slog.d(TAG, "napNoUpdateLocked: eventTime=" + eventTime + ", uid=" + uid); + Slog.d(TAG, "dreamDisplayGroupNoUpdateLocked: eventTime=" + eventTime + + ", uid=" + uid); } if (eventTime < mLastWakeTime || getWakefulnessLocked() != WAKEFULNESS_AWAKE @@ -1836,36 +1856,42 @@ public final class PowerManagerService extends SystemService return false; } - Trace.traceBegin(Trace.TRACE_TAG_POWER, "nap"); + Trace.traceBegin(Trace.TRACE_TAG_POWER, "napDisplayGroup"); try { - Slog.i(TAG, "Nap time (uid " + uid +")..."); + Slog.i(TAG, "Napping display group (groupId=" + groupId + ", uid=" + uid + ")..."); + + mDisplayGroupPowerStateMapper.setSandmanSummoned(groupId, true); + setWakefulnessLocked(groupId, WAKEFULNESS_DREAMING, eventTime, uid, /* reason= */ + 0, /* opUid= */ 0, /* opPackageName= */ null, /* details= */ null); + mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED; - mSandmanSummoned = true; - setWakefulnessLocked(WAKEFULNESS_DREAMING, 0, eventTime); } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } return true; } - // Done dozing, drop everything and go to sleep. - private boolean reallyGoToSleepNoUpdateLocked(long eventTime, int uid) { + private boolean reallySleepDisplayGroupNoUpdateLocked(int groupId, long eventTime, int uid) { if (DEBUG_SPEW) { - Slog.d(TAG, "reallyGoToSleepNoUpdateLocked: eventTime=" + eventTime + Slog.d(TAG, "reallySleepDisplayGroupNoUpdateLocked: eventTime=" + eventTime + ", uid=" + uid); } if (eventTime < mLastWakeTime || getWakefulnessLocked() == WAKEFULNESS_ASLEEP - || !mBootCompleted || !mSystemReady) { + || !mBootCompleted || !mSystemReady + || mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId) + == WAKEFULNESS_ASLEEP) { return false; } - Trace.traceBegin(Trace.TRACE_TAG_POWER, "reallyGoToSleep"); + Trace.traceBegin(Trace.TRACE_TAG_POWER, "reallySleepDisplayGroup"); try { - Slog.i(TAG, "Sleeping (uid " + uid +")..."); + Slog.i(TAG, "Sleeping display group (groupId=" + groupId + ", uid=" + uid + ")..."); - setWakefulnessLocked(WAKEFULNESS_ASLEEP, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, - eventTime); + setWakefulnessLocked(groupId, WAKEFULNESS_ASLEEP, eventTime, uid, + PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, /* opUid= */ 0, + /* opPackageName= */ null, /* details= */ null); + mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED; } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } @@ -1873,8 +1899,62 @@ public final class PowerManagerService extends SystemService } @VisibleForTesting - void setWakefulnessLocked(int wakefulness, int reason, long eventTime) { - if (getWakefulnessLocked() != wakefulness) { + void setWakefulnessLocked(int groupId, int wakefulness, long eventTime, int uid, int reason, + int opUid, String opPackageName, String details) { + if (mDisplayGroupPowerStateMapper.setWakefulnessLocked(groupId, wakefulness)) { + setGlobalWakefulnessLocked(mDisplayGroupPowerStateMapper.getGlobalWakefulnessLocked(), + eventTime, reason, uid, opUid, opPackageName, details); + } + } + + private void setGlobalWakefulnessLocked(int wakefulness, long eventTime, int reason, int uid, + int opUid, String opPackageName, String details) { + if (getWakefulnessLocked() == wakefulness) { + return; + } + + // Phase 1: Handle pre-wakefulness change bookkeeping. + final String traceMethodName; + switch (wakefulness) { + case WAKEFULNESS_ASLEEP: + traceMethodName = "reallyGoToSleep"; + Slog.i(TAG, "Sleeping (uid " + uid + ")..."); + break; + + case WAKEFULNESS_AWAKE: + traceMethodName = "wakeUp"; + Slog.i(TAG, "Waking up from " + + PowerManagerInternal.wakefulnessToString(getWakefulnessLocked()) + + " (uid=" + uid + + ", reason=" + PowerManager.wakeReasonToString(reason) + + ", details=" + details + + ")..."); + mLastWakeTime = eventTime; + mLastWakeReason = reason; + break; + + case WAKEFULNESS_DREAMING: + traceMethodName = "nap"; + Slog.i(TAG, "Nap time (uid " + uid + ")..."); + break; + + case WAKEFULNESS_DOZING: + traceMethodName = "goToSleep"; + Slog.i(TAG, "Going to sleep due to " + PowerManager.sleepReasonToString(reason) + + " (uid " + uid + ")..."); + + mLastSleepTime = eventTime; + mLastSleepReason = reason; + mDozeStartInProgress = true; + break; + + default: + throw new IllegalArgumentException("Unexpected wakefulness: " + wakefulness); + } + + Trace.traceBegin(Trace.TRACE_TAG_POWER, traceMethodName); + try { + // Phase 2: Handle wakefulness change and bookkeeping. // Under lock, invalidate before set ensures caches won't return stale values. mInjector.invalidateIsInteractiveCaches(); mWakefulnessRaw = wakefulness; @@ -1888,6 +1968,37 @@ public final class PowerManagerService extends SystemService mNotifier.onWakefulnessChangeStarted(wakefulness, reason, eventTime); } mAttentionDetector.onWakefulnessChangeStarted(wakefulness); + + // Phase 3: Handle post-wakefulness change bookkeeping. + switch (wakefulness) { + case WAKEFULNESS_AWAKE: + mNotifier.onWakeUp(reason, details, uid, opPackageName, opUid); + userActivityNoUpdateLocked( + eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid); + if (sQuiescent) { + mDirty |= DIRTY_QUIESCENT; + } + break; + + case WAKEFULNESS_DOZING: + // Report the number of wake locks that will be cleared by going to sleep. + int numWakeLocksCleared = 0; + final int numWakeLocks = mWakeLocks.size(); + for (int i = 0; i < numWakeLocks; i++) { + final WakeLock wakeLock = mWakeLocks.get(i); + switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) { + case PowerManager.FULL_WAKE_LOCK: + case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: + case PowerManager.SCREEN_DIM_WAKE_LOCK: + numWakeLocksCleared += 1; + break; + } + } + EventLogTags.writePowerSleepRequested(numWakeLocksCleared); + break; + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); } } @@ -1910,7 +2021,7 @@ public final class PowerManagerService extends SystemService } private void finishWakefulnessChangeIfNeededLocked() { - if (mWakefulnessChanging && mDisplayReady) { + if (mWakefulnessChanging && mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) { if (getWakefulnessLocked() == WAKEFULNESS_DOZING && (mWakeLockSummary & WAKE_LOCK_DOZE) == 0) { return; // wait until dream has enabled dozing @@ -1922,13 +2033,6 @@ public final class PowerManagerService extends SystemService || getWakefulnessLocked() == WAKEFULNESS_ASLEEP) { logSleepTimeoutRecapturedLocked(); } - if (getWakefulnessLocked() == WAKEFULNESS_AWAKE) { - Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0); - final int latencyMs = (int) (mClock.uptimeMillis() - mLastWakeTime); - if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) { - Slog.w(TAG, "Screen on took " + latencyMs + " ms"); - } - } mWakefulnessChanging = false; mNotifier.onWakefulnessChangeFinished(); } @@ -2027,7 +2131,6 @@ public final class PowerManagerService extends SystemService if ((dirty & DIRTY_BATTERY_STATE) != 0) { final boolean wasPowered = mIsPowered; final int oldPlugType = mPlugType; - final boolean oldLevelLow = mBatteryLevelLow; mIsPowered = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY); mPlugType = mBatteryManagerInternal.getPlugType(); mBatteryLevel = mBatteryManagerInternal.getBatteryLevel(); @@ -2056,7 +2159,8 @@ public final class PowerManagerService extends SystemService final long now = mClock.uptimeMillis(); if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType, dockedOnWirelessCharger)) { - wakeUpNoUpdateLocked(now, PowerManager.WAKE_REASON_PLUGGED_IN, + wakeDisplayGroupNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, now, + PowerManager.WAKE_REASON_PLUGGED_IN, "android.server.power:PLUGGED:" + mIsPowered, Process.SYSTEM_UID, mContext.getOpPackageName(), Process.SYSTEM_UID); } @@ -2340,7 +2444,8 @@ public final class PowerManagerService extends SystemService nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout; if (now < nextTimeout) { final DisplayPowerRequest displayPowerRequest = - mDisplayPowerRequestMapper.get(Display.DEFAULT_DISPLAY); + mDisplayGroupPowerStateMapper.getPowerRequestLocked( + Display.DEFAULT_DISPLAY_GROUP); if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT || displayPowerRequest.policy == DisplayPowerRequest.POLICY_VR) { mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT; @@ -2605,13 +2710,23 @@ public final class PowerManagerService extends SystemService } final long time = mClock.uptimeMillis(); if (isAttentiveTimeoutExpired(time)) { - changed = goToSleepNoUpdateLocked(time, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, - PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID); + // TODO (b/175764389): Support per-display timeouts. + for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { + changed = sleepDisplayGroupNoUpdateLocked(id, time, + PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, + PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID); + } } else if (shouldNapAtBedTimeLocked()) { - changed = napNoUpdateLocked(time, Process.SYSTEM_UID); + // TODO (b/175764389): Support per-display timeouts. + for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { + changed = dreamDisplayGroupNoUpdateLocked(id, time, Process.SYSTEM_UID); + } } else { - changed = goToSleepNoUpdateLocked(time, - PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID); + // TODO (b/175764389): Support per-display timeouts. + for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { + changed = sleepDisplayGroupNoUpdateLocked(id, time, + PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID); + } } } } @@ -2686,6 +2801,7 @@ public final class PowerManagerService extends SystemService private void updateDreamLocked(int dirty, boolean displayBecameReady) { if ((dirty & (DIRTY_WAKEFULNESS | DIRTY_USER_ACTIVITY + | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_ATTENTIVE | DIRTY_WAKE_LOCKS | DIRTY_BOOT_COMPLETED @@ -2694,7 +2810,7 @@ public final class PowerManagerService extends SystemService | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE | DIRTY_BATTERY_STATE)) != 0 || displayBecameReady) { - if (mDisplayReady) { + if (mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) { scheduleSandmanLocked(); } } @@ -2709,6 +2825,14 @@ public final class PowerManagerService extends SystemService } } + private void handleSandman() { + for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { + if (mDisplayGroupPowerStateMapper.isSandmanSupported(id)) { + handleSandman(id); + } + } + } + /** * Called when the device enters or exits a dreaming or dozing state. * @@ -2716,16 +2840,19 @@ public final class PowerManagerService extends SystemService * the dream and we don't want to hold our lock while doing so. There is a risk that * the device will wake or go to sleep in the meantime so we have to handle that case. */ - private void handleSandman() { // runs on handler thread + private void handleSandman(int groupId) { // runs on handler thread // Handle preconditions. final boolean startDreaming; final int wakefulness; synchronized (mLock) { mSandmanScheduled = false; + // TODO (b/175764708): Support per-display doze. wakefulness = getWakefulnessLocked(); - if (mSandmanSummoned && mDisplayReady) { - startDreaming = canDreamLocked() || canDozeLocked(); - mSandmanSummoned = false; + if ((wakefulness == WAKEFULNESS_DREAMING || wakefulness == WAKEFULNESS_DOZING) && + mDisplayGroupPowerStateMapper.isSandmanSummoned(groupId) + && mDisplayGroupPowerStateMapper.isReady(groupId)) { + startDreaming = canDreamLocked(groupId) || canDozeLocked(); + mDisplayGroupPowerStateMapper.setSandmanSummoned(groupId, false); } else { startDreaming = false; } @@ -2764,14 +2891,15 @@ public final class PowerManagerService extends SystemService // If preconditions changed, wait for the next iteration to determine // whether the dream should continue (or be restarted). - if (mSandmanSummoned || getWakefulnessLocked() != wakefulness) { + if (mDisplayGroupPowerStateMapper.isSandmanSummoned(groupId) + || mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId) != wakefulness) { return; // wait for next cycle } // Determine whether the dream should continue. long now = mClock.uptimeMillis(); if (wakefulness == WAKEFULNESS_DREAMING) { - if (isDreaming && canDreamLocked()) { + if (isDreaming && canDreamLocked(groupId)) { if (mDreamsBatteryLevelDrainCutoffConfig >= 0 && mBatteryLevel < mBatteryLevelWhenDreamStarted - mDreamsBatteryLevelDrainCutoffConfig @@ -2791,16 +2919,13 @@ public final class PowerManagerService extends SystemService // Dream has ended or will be stopped. Update the power state. if (isItBedTimeYetLocked()) { - int flags = 0; - if (isAttentiveTimeoutExpired(now)) { - flags |= PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE; - } - goToSleepNoUpdateLocked(now, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, flags, - Process.SYSTEM_UID); + final int flags = isAttentiveTimeoutExpired(now) + ? PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE : 0; + sleepDisplayGroupNoUpdateLocked(groupId, now, + PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, flags, Process.SYSTEM_UID); updatePowerStateLocked(); } else { - wakeUpNoUpdateLocked(now, - PowerManager.WAKE_REASON_UNKNOWN, + wakeDisplayGroupNoUpdateLocked(groupId, now, PowerManager.WAKE_REASON_UNKNOWN, "android.server.power:DREAM_FINISHED", Process.SYSTEM_UID, mContext.getOpPackageName(), Process.SYSTEM_UID); updatePowerStateLocked(); @@ -2811,7 +2936,7 @@ public final class PowerManagerService extends SystemService } // Doze has ended or will be stopped. Update the power state. - reallyGoToSleepNoUpdateLocked(now, Process.SYSTEM_UID); + reallySleepDisplayGroupNoUpdateLocked(groupId, now, Process.SYSTEM_UID); updatePowerStateLocked(); } } @@ -2823,11 +2948,11 @@ public final class PowerManagerService extends SystemService } /** - * Returns true if the device is allowed to dream in its current state. + * Returns true if the {@code groupId} is allowed to dream in its current state. */ - private boolean canDreamLocked() { + private boolean canDreamLocked(int groupId) { final DisplayPowerRequest displayPowerRequest = - mDisplayPowerRequestMapper.get(Display.DEFAULT_DISPLAY); + mDisplayGroupPowerStateMapper.getPowerRequestLocked(groupId); if (getWakefulnessLocked() != WAKEFULNESS_DREAMING || !mDreamsSupportedConfig || !mDreamsEnabledSetting @@ -2865,95 +2990,117 @@ public final class PowerManagerService extends SystemService /** * Updates the display power state asynchronously. - * When the update is finished, mDisplayReady will be set to true. The display - * controller posts a message to tell us when the actual display power state + * When the update is finished, the ready state of the displays will be updated. The display + * controllers post a message to tell us when the actual display power state * has been updated so we come back here to double-check and finish up. * * This function recalculates the display power state each time. * - * @return true if the display became ready. + * @return {@code true} if all displays became ready; {@code false} otherwise */ private boolean updateDisplayPowerStateLocked(int dirty) { - final boolean oldDisplayReady = mDisplayReady; + final boolean oldDisplayReady = mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked(); if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_VR_MODE_CHANGED | - DIRTY_QUIESCENT)) != 0) { + DIRTY_QUIESCENT | DIRTY_DISPLAY_GROUP_POWER_UPDATED)) != 0) { if ((dirty & DIRTY_QUIESCENT) != 0) { - if (mDisplayReady) { + if (mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) { sQuiescent = false; } else { mDirty |= DIRTY_QUIESCENT; } } - final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get( - Display.DEFAULT_DISPLAY); - displayPowerRequest.policy = getDesiredScreenPolicyLocked(); - - // Determine appropriate screen brightness and auto-brightness adjustments. - final boolean autoBrightness; - final float screenBrightnessOverride; - if (!mBootCompleted) { - // Keep the brightness steady during boot. This requires the - // bootloader brightness and the default brightness to be identical. - autoBrightness = false; - screenBrightnessOverride = mScreenBrightnessDefault; - } else if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) { - autoBrightness = false; - screenBrightnessOverride = mScreenBrightnessOverrideFromWindowManager; - } else { - autoBrightness = (mScreenBrightnessModeSetting == - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - screenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT; - } + for (final int groupId : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { + final DisplayPowerRequest displayPowerRequest = + mDisplayGroupPowerStateMapper.getPowerRequestLocked(groupId); + displayPowerRequest.policy = getDesiredScreenPolicyLocked(groupId); + + // Determine appropriate screen brightness and auto-brightness adjustments. + final boolean autoBrightness; + final float screenBrightnessOverride; + if (!mBootCompleted) { + // Keep the brightness steady during boot. This requires the + // bootloader brightness and the default brightness to be identical. + autoBrightness = false; + screenBrightnessOverride = mScreenBrightnessDefault; + } else if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) { + autoBrightness = false; + screenBrightnessOverride = mScreenBrightnessOverrideFromWindowManager; + } else { + autoBrightness = (mScreenBrightnessModeSetting + == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + screenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT; + } - // Update display power request. - displayPowerRequest.screenBrightnessOverride = screenBrightnessOverride; - displayPowerRequest.useAutoBrightness = autoBrightness; - displayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked(); - displayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness(); + // Update display power request. + displayPowerRequest.screenBrightnessOverride = screenBrightnessOverride; + displayPowerRequest.useAutoBrightness = autoBrightness; + displayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked(); + displayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness(); - updatePowerRequestFromBatterySaverPolicy(displayPowerRequest); + updatePowerRequestFromBatterySaverPolicy(displayPowerRequest); - if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) { - displayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager; - if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0 - && !mDrawWakeLockOverrideFromSidekick) { - if (displayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) { - displayPowerRequest.dozeScreenState = Display.STATE_DOZE; - } - if (displayPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND) { - displayPowerRequest.dozeScreenState = Display.STATE_ON; + if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) { + displayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager; + if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0 + && !mDrawWakeLockOverrideFromSidekick) { + if (displayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) { + displayPowerRequest.dozeScreenState = Display.STATE_DOZE; + } + if (displayPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND) { + displayPowerRequest.dozeScreenState = Display.STATE_ON; + } } + displayPowerRequest.dozeScreenBrightness = + mDozeScreenBrightnessOverrideFromDreamManagerFloat; + } else { + displayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN; + displayPowerRequest.dozeScreenBrightness = + PowerManager.BRIGHTNESS_INVALID_FLOAT; } - displayPowerRequest.dozeScreenBrightness = - mDozeScreenBrightnessOverrideFromDreamManagerFloat; - } else { - displayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN; - displayPowerRequest.dozeScreenBrightness = - PowerManager.BRIGHTNESS_INVALID_FLOAT; - } - mDisplayReady = mDisplayManagerInternal.requestPowerState(Display.DEFAULT_DISPLAY_GROUP, - displayPowerRequest, mRequestWaitForNegativeProximity); - mRequestWaitForNegativeProximity = false; + final boolean ready = mDisplayManagerInternal.requestPowerState(groupId, + displayPowerRequest, mRequestWaitForNegativeProximity); - if (DEBUG_SPEW) { - Slog.d(TAG, "updateDisplayPowerStateLocked: mDisplayReady=" + mDisplayReady - + ", policy=" + displayPowerRequest.policy - + ", mWakefulness=" + getWakefulnessLocked() - + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary) - + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary) - + ", mBootCompleted=" + mBootCompleted - + ", screenBrightnessOverride=" + screenBrightnessOverride - + ", useAutoBrightness=" + autoBrightness - + ", mScreenBrightnessBoostInProgress=" + mScreenBrightnessBoostInProgress - + ", mIsVrModeEnabled= " + mIsVrModeEnabled - + ", sQuiescent=" + sQuiescent); + if (DEBUG_SPEW) { + Slog.d(TAG, "updateDisplayPowerStateLocked: displayReady=" + ready + + ", groupId=" + groupId + + ", policy=" + policyToString(displayPowerRequest.policy) + + ", mWakefulness=" + + PowerManagerInternal.wakefulnessToString( + mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId)) + + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary) + + ", mUserActivitySummary=0x" + Integer.toHexString( + mUserActivitySummary) + + ", mBootCompleted=" + mBootCompleted + + ", screenBrightnessOverride=" + + displayPowerRequest.screenBrightnessOverride + + ", useAutoBrightness=" + displayPowerRequest.useAutoBrightness + + ", mScreenBrightnessBoostInProgress=" + + mScreenBrightnessBoostInProgress + + ", mIsVrModeEnabled= " + mIsVrModeEnabled + + ", sQuiescent=" + sQuiescent); + } + + final boolean displayReadyStateChanged = + mDisplayGroupPowerStateMapper.setDisplayGroupReadyLocked(groupId, ready); + if (ready && displayReadyStateChanged + && mDisplayGroupPowerStateMapper.getWakefulnessLocked( + groupId) == WAKEFULNESS_AWAKE) { + Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, groupId); + final int latencyMs = (int) (mClock.uptimeMillis() + - mDisplayGroupPowerStateMapper.getLastPowerOnTimeLocked(groupId)); + if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) { + Slog.w(TAG, "Screen on took " + latencyMs + " ms"); + } + } } + mRequestWaitForNegativeProximity = false; } - return mDisplayReady && !oldDisplayReady; + + return mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked() && !oldDisplayReady; } private void updateScreenBrightnessBoostLocked(int dirty) { @@ -2987,12 +3134,11 @@ public final class PowerManagerService extends SystemService } @VisibleForTesting - int getDesiredScreenPolicyLocked() { - if (getWakefulnessLocked() == WAKEFULNESS_ASLEEP || sQuiescent) { + int getDesiredScreenPolicyLocked(int groupId) { + final int wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId); + if (wakefulness == WAKEFULNESS_ASLEEP || sQuiescent) { return DisplayPowerRequest.POLICY_OFF; - } - - if (getWakefulnessLocked() == WAKEFULNESS_DOZING) { + } else if (wakefulness == WAKEFULNESS_DOZING) { if ((mWakeLockSummary & WAKE_LOCK_DOZE) != 0) { return DisplayPowerRequest.POLICY_DOZE; } @@ -3022,7 +3168,6 @@ public final class PowerManagerService extends SystemService private final DisplayManagerInternal.DisplayPowerCallbacks mDisplayPowerCallbacks = new DisplayManagerInternal.DisplayPowerCallbacks() { - private int mDisplayState = Display.STATE_UNKNOWN; @Override public void onStateChanged() { @@ -3053,29 +3198,25 @@ public final class PowerManagerService extends SystemService } @Override - public void onDisplayStateChange(int state) { + public void onDisplayStateChange(boolean allInactive, boolean allOff) { // This method is only needed to support legacy display blanking behavior // where the display's power state is coupled to suspend or to the power HAL. // The order of operations matters here. synchronized (mLock) { - if (mDisplayState != state) { - mDisplayState = state; - setPowerModeInternal(MODE_DISPLAY_INACTIVE, - !Display.isActiveState(state)); - if (state == Display.STATE_OFF) { - if (!mDecoupleHalInteractiveModeFromDisplayConfig) { - setHalInteractiveModeLocked(false); - } - if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) { - setHalAutoSuspendModeLocked(true); - } - } else { - if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) { - setHalAutoSuspendModeLocked(false); - } - if (!mDecoupleHalInteractiveModeFromDisplayConfig) { - setHalInteractiveModeLocked(true); - } + setPowerModeInternal(MODE_DISPLAY_INACTIVE, allInactive); + if (allOff) { + if (!mDecoupleHalInteractiveModeFromDisplayConfig) { + setHalInteractiveModeLocked(false); + } + if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) { + setHalAutoSuspendModeLocked(true); + } + } else { + if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) { + setHalAutoSuspendModeLocked(false); + } + if (!mDecoupleHalInteractiveModeFromDisplayConfig) { + setHalInteractiveModeLocked(true); } } } @@ -3090,13 +3231,6 @@ public final class PowerManagerService extends SystemService public void releaseSuspendBlocker() { mDisplaySuspendBlocker.release(); } - - @Override - public String toString() { - synchronized (this) { - return "state=" + Display.stateToString(mDisplayState); - } - } }; private boolean shouldUseProximitySensorLocked() { @@ -3112,9 +3246,11 @@ public final class PowerManagerService extends SystemService final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0); final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked(); final boolean autoSuspend = !needDisplaySuspendBlocker; - final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get( - Display.DEFAULT_DISPLAY); - final boolean interactive = displayPowerRequest.isBrightOrDim(); + final int[] groupIds = mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked(); + boolean interactive = false; + for (int id : groupIds) { + interactive |= mDisplayGroupPowerStateMapper.getPowerRequestLocked(id).isBrightOrDim(); + } // Disable auto-suspend if needed. // FIXME We should consider just leaving auto-suspend enabled forever since @@ -3144,7 +3280,7 @@ public final class PowerManagerService extends SystemService // until the display is actually ready so that all transitions have // completed. This is probably a good sign that things have gotten // too tangled over here... - if (interactive || mDisplayReady) { + if (interactive || mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) { setHalInteractiveModeLocked(interactive); } } @@ -3170,29 +3306,10 @@ public final class PowerManagerService extends SystemService * We do so if the screen is on or is in transition between states. */ private boolean needDisplaySuspendBlockerLocked() { - if (!mDisplayReady) { + if (!mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) { return true; } - final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get( - Display.DEFAULT_DISPLAY); - if (displayPowerRequest.isBrightOrDim()) { - // If we asked for the screen to be on but it is off due to the proximity - // sensor then we may suspend but only if the configuration allows it. - // On some hardware it may not be safe to suspend because the proximity - // sensor may not be correctly configured as a wake-up source. - if (!displayPowerRequest.useProximitySensor || !mProximityPositive - || !mSuspendWhenScreenOffDueToProximityConfig) { - return true; - } - } - if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE - && displayPowerRequest.dozeScreenState == Display.STATE_ON) { - // Although we are in DOZE and would normally allow the device to suspend, - // the doze service has explicitly requested the display to remain in the ON - // state which means we should hold the display suspend blocker. - return true; - } if (mScreenBrightnessBoostInProgress) { return true; } @@ -3206,6 +3323,30 @@ public final class PowerManagerService extends SystemService return true; } + final int[] groupIds = mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked(); + for (int id : groupIds) { + final DisplayPowerRequest displayPowerRequest = + mDisplayGroupPowerStateMapper.getPowerRequestLocked(id); + if (displayPowerRequest.isBrightOrDim()) { + // If we asked for the screen to be on but it is off due to the proximity + // sensor then we may suspend but only if the configuration allows it. + // On some hardware it may not be safe to suspend because the proximity + // sensor may not be correctly configured as a wake-up source. + if (!displayPowerRequest.useProximitySensor || !mProximityPositive + || !mSuspendWhenScreenOffDueToProximityConfig) { + return true; + } + } + + if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE + && displayPowerRequest.dozeScreenState == Display.STATE_ON) { + // Although we are in DOZE and would normally allow the device to suspend, + // the doze service has explicitly requested the display to remain in the ON + // state which means we should hold the display suspend blocker. + return true; + } + } + // Let the system suspend if the screen is off or dozing. return false; } @@ -3739,9 +3880,15 @@ public final class PowerManagerService extends SystemService synchronized (mLock) { mForceSuspendActive = true; // Place the system in an non-interactive state - goToSleepInternal(mClock.uptimeMillis(), - PowerManager.GO_TO_SLEEP_REASON_FORCE_SUSPEND, - PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, uid); + boolean updatePowerState = false; + for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { + updatePowerState |= sleepDisplayGroupNoUpdateLocked(id, mClock.uptimeMillis(), + PowerManager.GO_TO_SLEEP_REASON_FORCE_SUSPEND, + PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, uid); + } + if (updatePowerState) { + updatePowerStateLocked(); + } // Disable all the partial wake locks as well updateWakeLockDisabledStatesLocked(); @@ -3881,7 +4028,6 @@ public final class PowerManagerService extends SystemService pw.println(" mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)); pw.println(" mRequestWaitForNegativeProximity=" + mRequestWaitForNegativeProximity); pw.println(" mSandmanScheduled=" + mSandmanScheduled); - pw.println(" mSandmanSummoned=" + mSandmanSummoned); pw.println(" mBatteryLevelLow=" + mBatteryLevelLow); pw.println(" mLightDeviceIdleMode=" + mLightDeviceIdleMode); pw.println(" mDeviceIdleMode=" + mDeviceIdleMode); @@ -3899,7 +4045,6 @@ public final class PowerManagerService extends SystemService + TimeUtils.formatUptime(mLastScreenBrightnessBoostTime)); pw.println(" mScreenBrightnessBoostInProgress=" + mScreenBrightnessBoostInProgress); - pw.println(" mDisplayReady=" + mDisplayReady); pw.println(" mHoldingWakeLockSuspendBlocker=" + mHoldingWakeLockSuspendBlocker); pw.println(" mHoldingDisplaySuspendBlocker=" + mHoldingDisplaySuspendBlocker); pw.println(" mLastFlipTime=" + mLastFlipTime); @@ -4138,7 +4283,6 @@ public final class PowerManagerService extends SystemService PowerManagerServiceDumpProto.IS_REQUEST_WAIT_FOR_NEGATIVE_PROXIMITY, mRequestWaitForNegativeProximity); proto.write(PowerManagerServiceDumpProto.IS_SANDMAN_SCHEDULED, mSandmanScheduled); - proto.write(PowerManagerServiceDumpProto.IS_SANDMAN_SUMMONED, mSandmanSummoned); proto.write(PowerManagerServiceDumpProto.IS_BATTERY_LEVEL_LOW, mBatteryLevelLow); proto.write(PowerManagerServiceDumpProto.IS_LIGHT_DEVICE_IDLE_MODE, mLightDeviceIdleMode); proto.write(PowerManagerServiceDumpProto.IS_DEVICE_IDLE_MODE, mDeviceIdleMode); @@ -4165,7 +4309,6 @@ public final class PowerManagerService extends SystemService proto.write( PowerManagerServiceDumpProto.IS_SCREEN_BRIGHTNESS_BOOST_IN_PROGRESS, mScreenBrightnessBoostInProgress); - proto.write(PowerManagerServiceDumpProto.IS_DISPLAY_READY, mDisplayReady); proto.write( PowerManagerServiceDumpProto.IS_HOLDING_WAKE_LOCK_SUSPEND_BLOCKER, mHoldingWakeLockSuspendBlocker); @@ -4979,7 +5122,8 @@ public final class PowerManagerService extends SystemService final int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); try { - wakeUpInternal(eventTime, reason, details, uid, opPackageName, uid); + wakeDisplayGroup(Display.DEFAULT_DISPLAY_GROUP, eventTime, reason, details, uid, + opPackageName, uid); } finally { Binder.restoreCallingIdentity(ident); } @@ -4997,7 +5141,7 @@ public final class PowerManagerService extends SystemService final int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); try { - goToSleepInternal(eventTime, reason, flags, uid); + sleepDisplayGroup(Display.DEFAULT_DISPLAY_GROUP, eventTime, reason, flags, uid); } finally { Binder.restoreCallingIdentity(ident); } @@ -5015,7 +5159,7 @@ public final class PowerManagerService extends SystemService final int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); try { - napInternal(eventTime, uid); + dreamDisplayGroup(Display.DEFAULT_DISPLAY_GROUP, eventTime, uid); } finally { Binder.restoreCallingIdentity(ident); } @@ -5677,7 +5821,8 @@ public final class PowerManagerService extends SystemService // also tells us that we're not already ignoring the proximity sensor. final DisplayPowerRequest displayPowerRequest = - mDisplayPowerRequestMapper.get(Display.DEFAULT_DISPLAY); + mDisplayGroupPowerStateMapper.getPowerRequestLocked( + Display.DEFAULT_DISPLAY_GROUP); if (displayPowerRequest.useProximitySensor && mProximityPositive) { mDisplayManagerInternal.ignoreProximitySensorUntilChanged(); return true; diff --git a/services/core/java/com/android/server/speech/Android.bp b/services/core/java/com/android/server/speech/Android.bp index 379b0754b82a..5605349812da 100644 --- a/services/core/java/com/android/server/speech/Android.bp +++ b/services/core/java/com/android/server/speech/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + filegroup { name: "services.speech-sources", srcs: ["java/**/*.java"], diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index a390df9edae1..8ffbb0a87dc0 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -1336,7 +1336,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } @Override - public void onNotificationClear(String pkg, String tag, int id, int userId, String key, + public void onNotificationClear(String pkg, int userId, String key, @NotificationStats.DismissalSurface int dismissalSurface, @NotificationStats.DismissalSentiment int dismissalSentiment, NotificationVisibility nv) { @@ -1345,7 +1345,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D final int callingPid = Binder.getCallingPid(); final long identity = Binder.clearCallingIdentity(); try { - mNotificationDelegate.onNotificationClear(callingUid, callingPid, pkg, tag, id, userId, + mNotificationDelegate.onNotificationClear(callingUid, callingPid, pkg, userId, key, dismissalSurface, dismissalSentiment, nv); } finally { Binder.restoreCallingIdentity(identity); diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index 9ee072ee7ce5..06748a3aa2d1 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -969,7 +969,7 @@ public class VcnGatewayConnection extends StateMachine { mGatewayStatusCallback.onGatewayConnectionError( mConnectionConfig.getRequiredUnderlyingCapabilities(), VCN_ERROR_CODE_INTERNAL_ERROR, - "java.lang.RuntimeException", + RuntimeException.class.getName(), "Received " + exception.getClass().getSimpleName() + " with message: " @@ -991,11 +991,11 @@ public class VcnGatewayConnection extends StateMachine { } else if (exception instanceof IkeInternalException && exception.getCause() instanceof IOException) { errorCode = VCN_ERROR_CODE_NETWORK_ERROR; - exceptionClass = "java.io.IOException"; + exceptionClass = IOException.class.getName(); exceptionMessage = exception.getCause().getMessage(); } else { errorCode = VCN_ERROR_CODE_INTERNAL_ERROR; - exceptionClass = "java.lang.RuntimeException"; + exceptionClass = RuntimeException.class.getName(); exceptionMessage = "Received " + exception.getClass().getSimpleName() diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 5d3b9c197267..dd9619ae815a 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -3624,7 +3624,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } // Reset the last saved PiP snap fraction on removal. - mDisplayContent.mPinnedStackControllerLocked.onActivityHidden(mActivityComponent); + mDisplayContent.mPinnedTaskControllerLocked.onActivityHidden(mActivityComponent); mWmService.mEmbeddedWindowController.onActivityRemoved(this); mRemovingFromDisplay = false; } @@ -4976,7 +4976,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ProtoLog.v(WM_DEBUG_ADD_REMOVE, "notifyAppStopped: %s", this); mAppStopped = true; // Reset the last saved PiP snap fraction on app stop. - mDisplayContent.mPinnedStackControllerLocked.onActivityHidden(mActivityComponent); + mDisplayContent.mPinnedTaskControllerLocked.onActivityHidden(mActivityComponent); destroySurfaces(); // Remove any starting window that was added for this app if they are still around. removeStartingWindow(); diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java index f505daa9b628..a7312b321e4d 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java @@ -67,39 +67,40 @@ import java.util.function.BiFunction; * .setImeContainer(imeContainer) * .setTaskDisplayAreas(rootTdaList); * - * // Build root hierarchy of front and rear DisplayAreaGroup. - * RootDisplayArea frontRoot = new RootDisplayArea(wmService, "FrontRoot", FEATURE_FRONT_ROOT); - * DisplayAreaPolicyBuilder.HierarchyBuilder frontGroupHierarchy = - * new DisplayAreaPolicyBuilder.HierarchyBuilder(frontRoot) + * // Build root hierarchy of first and second DisplayAreaGroup. + * RootDisplayArea firstRoot = new RootDisplayArea(wmService, "FirstRoot", FEATURE_FIRST_ROOT); + * DisplayAreaPolicyBuilder.HierarchyBuilder firstGroupHierarchy = + * new DisplayAreaPolicyBuilder.HierarchyBuilder(firstRoot) * // (Optional) .addFeature(...) - * .setTaskDisplayAreas(frontTdaList); + * .setTaskDisplayAreas(firstTdaList); * - * RootDisplayArea rearRoot = new RootDisplayArea(wmService, "RearRoot", FEATURE_REAR_ROOT); - * DisplayAreaPolicyBuilder.HierarchyBuilder rearGroupHierarchy = - * new DisplayAreaPolicyBuilder.HierarchyBuilder(rearRoot) + * RootDisplayArea secondRoot = new RootDisplayArea(wmService, "SecondRoot", + * FEATURE_REAR_ROOT); + * DisplayAreaPolicyBuilder.HierarchyBuilder secondGroupHierarchy = + * new DisplayAreaPolicyBuilder.HierarchyBuilder(secondRoot) * // (Optional) .addFeature(...) - * .setTaskDisplayAreas(rearTdaList); + * .setTaskDisplayAreas(secondTdaList); * * // Define the function to select root for window to attach. * BiFunction<WindowToken, Bundle, RootDisplayArea> selectRootForWindowFunc = - * (windowToken, bundle) -> { - * if (bundle == null) { + * (windowToken, options) -> { + * if (options == null) { * return root; * } * // OEMs need to define the condition. * if (...) { - * return frontRoot; + * return firstRoot; * } * if (...) { - * return rearRoot; + * return secondRoot; * } * return root; * }; * * return new DisplayAreaPolicyBuilder() * .setRootHierarchy(rootHierarchy) - * .addDisplayAreaGroupHierarchy(frontGroupHierarchy) - * .addDisplayAreaGroupHierarchy(rearGroupHierarchy) + * .addDisplayAreaGroupHierarchy(firstGroupHierarchy) + * .addDisplayAreaGroupHierarchy(secondGroupHierarchy) * .setSelectRootForWindowFunc(selectRootForWindowFunc) * .build(wmService, content); * </pre> @@ -110,12 +111,12 @@ import java.util.function.BiFunction; * - WindowedMagnification * - DisplayArea.Tokens (Wallpapers can be attached here) * - TaskDisplayArea - * - RootDisplayArea (FrontRoot) + * - RootDisplayArea (FirstRoot) * - DisplayArea.Tokens (Wallpapers can be attached here) * - TaskDisplayArea * - DisplayArea.Tokens (windows above Tasks up to IME can be attached here) * - DisplayArea.Tokens (windows above IME can be attached here) - * - RootDisplayArea (RearRoot) + * - RootDisplayArea (SecondRoot) * - DisplayArea.Tokens (Wallpapers can be attached here) * - TaskDisplayArea * - DisplayArea.Tokens (windows above Tasks up to IME can be attached here) @@ -133,14 +134,23 @@ import java.util.function.BiFunction; * {@link RootDisplayArea}. */ class DisplayAreaPolicyBuilder { + + /** + * Key to specify the {@link RootDisplayArea} to attach the window to. Should be used by the + * function passed in from {@link #setSelectRootForWindowFunc(BiFunction)} + */ + static final String KEY_ROOT_DISPLAY_AREA_ID = "root_display_area_id"; + @Nullable private HierarchyBuilder mRootHierarchyBuilder; - private ArrayList<HierarchyBuilder> mDisplayAreaGroupHierarchyBuilders = new ArrayList<>(); + private final ArrayList<HierarchyBuilder> mDisplayAreaGroupHierarchyBuilders = + new ArrayList<>(); /** * When a window is created, the policy will use this function, which takes window type and * options, to select the {@link RootDisplayArea} to place that window in. The selected root * can be either the one of the {@link #mRootHierarchyBuilder} or the one of any of the * {@link #mDisplayAreaGroupHierarchyBuilders}. + * @see DefaultSelectRootForWindowFunction as an example. **/ @Nullable private BiFunction<Integer, Bundle, RootDisplayArea> mSelectRootForWindowFunc; @@ -160,7 +170,10 @@ class DisplayAreaPolicyBuilder { return this; } - /** The policy will use this function to find the root to place windows in. */ + /** + * The policy will use this function to find the root to place windows in. + * @see DefaultSelectRootForWindowFunction as an example. + */ DisplayAreaPolicyBuilder setSelectRootForWindowFunc( BiFunction<Integer, Bundle, RootDisplayArea> selectRootForWindowFunc) { mSelectRootForWindowFunc = selectRootForWindowFunc; @@ -169,7 +182,7 @@ class DisplayAreaPolicyBuilder { /** * Makes sure the setting meets the requirement: - * 1. {@link mRootHierarchyBuilder} must be set. + * 1. {@link #mRootHierarchyBuilder} must be set. * 2. {@link RootDisplayArea} and {@link TaskDisplayArea} must have unique ids. * 3. {@link Feature} below the same {@link RootDisplayArea} must have unique ids. * 4. There must be exactly one {@link HierarchyBuilder} that contains the IME container. @@ -309,11 +322,57 @@ class DisplayAreaPolicyBuilder { hierarchyBuilder.build(); displayAreaGroupRoots.add(hierarchyBuilder.mRoot); } + // Use the default function if it is not specified otherwise. + if (mSelectRootForWindowFunc == null) { + mSelectRootForWindowFunc = new DefaultSelectRootForWindowFunction( + mRootHierarchyBuilder.mRoot, displayAreaGroupRoots); + } return new Result(wmService, mRootHierarchyBuilder.mRoot, displayAreaGroupRoots, mSelectRootForWindowFunc); } /** + * The default function to be used for finding {@link RootDisplayArea} for window to be attached + * to if there is no other function set through {@link #setSelectRootForWindowFunc(BiFunction)}. + * + * When a window is created with {@link Bundle} options, this function will select the + * {@link RootDisplayArea} based on the options. Returns {@link #mDisplayRoot} if there is no + * match found. + */ + private static class DefaultSelectRootForWindowFunction implements + BiFunction<Integer, Bundle, RootDisplayArea> { + final RootDisplayArea mDisplayRoot; + final List<RootDisplayArea> mDisplayAreaGroupRoots; + + DefaultSelectRootForWindowFunction(RootDisplayArea displayRoot, + List<RootDisplayArea> displayAreaGroupRoots) { + mDisplayRoot = displayRoot; + mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots); + } + + @Override + public RootDisplayArea apply(Integer windowType, Bundle options) { + if (mDisplayAreaGroupRoots.isEmpty()) { + return mDisplayRoot; + } + + // Select the RootDisplayArea set in options. + if (options != null && options.containsKey(KEY_ROOT_DISPLAY_AREA_ID)) { + final int rootId = options.getInt(KEY_ROOT_DISPLAY_AREA_ID); + if (mDisplayRoot.mFeatureId == rootId) { + return mDisplayRoot; + } + for (int i = mDisplayAreaGroupRoots.size() - 1; i >= 0; i--) { + if (mDisplayAreaGroupRoots.get(i).mFeatureId == rootId) { + return mDisplayAreaGroupRoots.get(i); + } + } + } + return mDisplayRoot; + } + } + + /** * Builder to define {@link Feature} and {@link DisplayArea} hierarchy under a * {@link RootDisplayArea} */ @@ -672,15 +731,10 @@ class DisplayAreaPolicyBuilder { Result(WindowManagerService wmService, RootDisplayArea root, List<RootDisplayArea> displayAreaGroupRoots, - @Nullable BiFunction<Integer, Bundle, RootDisplayArea> - selectRootForWindowFunc) { + BiFunction<Integer, Bundle, RootDisplayArea> selectRootForWindowFunc) { super(wmService, root); mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots); - mSelectRootForWindowFunc = selectRootForWindowFunc == null - // Always return the highest level root of the logical display when the func is - // not specified. - ? (type, options) -> mRoot - : selectRootForWindowFunc; + mSelectRootForWindowFunc = selectRootForWindowFunc; // Cache the default TaskDisplayArea for quick access. mDefaultTaskDisplayArea = mRoot.getItemFromTaskDisplayAreas(taskDisplayArea -> diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 0143e70f53ca..eff4ea6536bd 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -36,6 +36,7 @@ import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import static android.os.Build.VERSION_CODES.N; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.util.DisplayMetrics.DENSITY_DEFAULT; +import static android.util.RotationUtils.deltaRotation; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD; import static android.view.Display.FLAG_PRIVATE; @@ -47,7 +48,6 @@ import static android.view.InsetsState.ITYPE_LEFT_GESTURES; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_RIGHT_GESTURES; import static android.view.Surface.ROTATION_0; -import static android.view.Surface.ROTATION_180; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import static android.view.View.GONE; @@ -150,7 +150,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerInternal; -import android.app.WindowConfiguration; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo.ScreenOrientation; @@ -158,10 +157,8 @@ import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Insets; -import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; -import android.graphics.RectF; import android.graphics.Region; import android.graphics.Region.Op; import android.hardware.HardwareBuffer; @@ -206,6 +203,7 @@ import android.view.MagnificationSpec; import android.view.RemoteAnimationDefinition; import android.view.RoundedCorners; import android.view.Surface; +import android.view.Surface.Rotation; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.SurfaceSession; @@ -449,8 +447,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp /** Save allocating when calculating rects */ private final Rect mTmpRect = new Rect(); private final Rect mTmpRect2 = new Rect(); - private final RectF mTmpRectF = new RectF(); - private final Matrix mTmpMatrix = new Matrix(); private final Region mTmpRegion = new Region(); /** Used for handing back size of display */ @@ -461,8 +457,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp /** Remove this display when animation on it has completed. */ private boolean mDeferredRemoval; - final DockedStackDividerController mDividerControllerLocked; - final PinnedStackController mPinnedStackControllerLocked; + final DockedTaskDividerController mDividerControllerLocked; + final PinnedTaskController mPinnedTaskControllerLocked; final ArrayList<WindowState> mTapExcludedWindows = new ArrayList<>(); /** A collection of windows that provide tap exclude regions inside of them. */ @@ -1012,8 +1008,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mDisplayPolicy.systemReady(); } mWindowCornerRadius = mDisplayPolicy.getWindowCornerRadius(); - mDividerControllerLocked = new DockedStackDividerController(this); - mPinnedStackControllerLocked = new PinnedStackController(mWmService, this); + mDividerControllerLocked = new DockedTaskDividerController(this); + mPinnedTaskControllerLocked = new PinnedTaskController(mWmService, this); final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession) .setOpaque(true) @@ -1289,7 +1285,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return mInsetsPolicy; } - @Surface.Rotation + @Rotation int getRotation() { return mDisplayRotation.getRotation(); } @@ -1479,7 +1475,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * Returns a valid rotation if the activity can use different orientation than the display. * Otherwise {@link #ROTATION_UNDEFINED}. */ - @Surface.Rotation + @Rotation int rotationForActivityInDifferentOrientation(@NonNull ActivityRecord r) { if (!WindowManagerService.ENABLE_FIXED_ROTATION_TRANSFORM) { return ROTATION_UNDEFINED; @@ -1567,7 +1563,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // has it own policy for bounds, the activity bounds based on parent is unknown. return false; } - if (mPinnedStackControllerLocked.isPipActiveOrWindowingModeChanging()) { + if (mPinnedTaskControllerLocked.isPipActiveOrWindowingModeChanging()) { // Use normal rotation animation because seamless PiP rotation is not supported yet. return false; } @@ -1614,7 +1610,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * Sets the provided record to {@link #mFixedRotationLaunchingApp} if possible to apply fixed * rotation transform to it and indicate that the display may be rotated after it is launched. */ - void setFixedRotationLaunchingApp(@NonNull ActivityRecord r, @Surface.Rotation int rotation) { + void setFixedRotationLaunchingApp(@NonNull ActivityRecord r, @Rotation int rotation) { final WindowToken prevRotatedLaunchingApp = mFixedRotationLaunchingApp; if (prevRotatedLaunchingApp == r && r.getWindowConfiguration().getRotation() == rotation) { @@ -2296,12 +2292,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } } - DockedStackDividerController getDockedDividerController() { + DockedTaskDividerController getDockedDividerController() { return mDividerControllerLocked; } - PinnedStackController getPinnedStackController() { - return mPinnedStackControllerLocked; + PinnedTaskController getPinnedTaskController() { + return mPinnedTaskControllerLocked; } /** @@ -2382,10 +2378,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * for bounds calculations. */ void preOnConfigurationChanged() { - final PinnedStackController pinnedStackController = getPinnedStackController(); + final PinnedTaskController pinnedTaskController = getPinnedTaskController(); - if (pinnedStackController != null) { - getPinnedStackController().onConfigurationChanged(); + if (pinnedTaskController != null) { + getPinnedTaskController().onConfigurationChanged(); } } @@ -2929,7 +2925,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp final boolean imeVisible = imeWin != null && imeWin.isVisible() && imeWin.isDisplayed(); final int imeHeight = getInputMethodWindowVisibleHeight(); - mPinnedStackControllerLocked.setAdjustedForIme(imeVisible, imeHeight); + mPinnedTaskControllerLocked.setAdjustedForIme(imeVisible, imeHeight); } int getInputMethodWindowVisibleHeight() { @@ -2951,27 +2947,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp forAllRootTasks(Task::prepareFreezingTaskBounds); } - void rotateBounds(int oldRotation, int newRotation, Rect bounds) { - getBounds(mTmpRect, newRotation); - rotateBounds(mTmpRect, oldRotation, newRotation, bounds); - } - - void rotateBounds(Rect parentBounds, int oldRotation, int newRotation, Rect bounds) { - // Compute a transform matrix to undo the coordinate space transformation, - // and present the window at the same physical position it previously occupied. - final int deltaRotation = deltaRotation(newRotation, oldRotation); - createRotationMatrix( - deltaRotation, parentBounds.width(), parentBounds.height(), mTmpMatrix); - - mTmpRectF.set(bounds); - mTmpMatrix.mapRect(mTmpRectF); - mTmpRectF.round(bounds); - } - - static int deltaRotation(int oldRotation, int newRotation) { - int delta = newRotation - oldRotation; - if (delta < 0) delta += 4; - return delta; + void rotateBounds(@Rotation int oldRotation, @Rotation int newRotation, Rect inOutBounds) { + // Get display bounds on oldRotation as parent bounds for the rotation. + getBounds(mTmpRect, oldRotation); + RotationUtils.rotateBounds(inOutBounds, mTmpRect, oldRotation, newRotation); } public void setRotationAnimation(ScreenRotationAnimation screenRotationAnimation) { @@ -2985,35 +2964,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return mScreenRotationAnimation; } - private static void createRotationMatrix(int rotation, float displayWidth, float displayHeight, - Matrix outMatrix) { - // For rotations without Z-ordering we don't need the target rectangle's position. - createRotationMatrix(rotation, 0 /* rectLeft */, 0 /* rectTop */, displayWidth, - displayHeight, outMatrix); - } - - static void createRotationMatrix(int rotation, float rectLeft, float rectTop, - float displayWidth, float displayHeight, Matrix outMatrix) { - switch (rotation) { - case ROTATION_0: - outMatrix.reset(); - break; - case ROTATION_270: - outMatrix.setRotate(270, 0, 0); - outMatrix.postTranslate(0, displayHeight); - outMatrix.postTranslate(rectTop, 0); - break; - case ROTATION_180: - outMatrix.reset(); - break; - case ROTATION_90: - outMatrix.setRotate(90, 0, 0); - outMatrix.postTranslate(displayWidth, 0); - outMatrix.postTranslate(-rectTop, rectLeft); - break; - } - } - @Override public void dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel) { @@ -3200,7 +3150,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } pw.println(); - mPinnedStackControllerLocked.dump(prefix, pw); + mPinnedTaskControllerLocked.dump(prefix, pw); pw.println(); mDisplayFrames.dump(prefix, pw); @@ -3639,8 +3589,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } private boolean isImeControlledByApp() { - return mImeInputTarget != null && !WindowConfiguration.isSplitScreenWindowingMode( - mImeInputTarget.getWindowingMode()); + return mImeInputTarget != null && !mImeInputTarget.inMultiWindowMode(); } boolean isImeAttachedToApp() { @@ -4289,17 +4238,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp out.set(left, top, left + width, top + height); } - private void getBounds(Rect out, int orientation) { + private void getBounds(Rect out, @Rotation int rotation) { getBounds(out); // Rotate the Rect if needed. final int currentRotation = mDisplayInfo.rotation; - final int rotationDelta = deltaRotation(currentRotation, orientation); + final int rotationDelta = deltaRotation(currentRotation, rotation); if (rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270) { - createRotationMatrix(rotationDelta, mBaseDisplayWidth, mBaseDisplayHeight, mTmpMatrix); - mTmpRectF.set(out); - mTmpMatrix.mapRect(mTmpRectF); - mTmpRectF.round(out); + out.set(0, 0, out.height(), out.width()); } } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 5460e36a5f1b..f64f04cc0829 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -24,6 +24,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.content.res.Configuration.UI_MODE_TYPE_CAR; import static android.content.res.Configuration.UI_MODE_TYPE_MASK; +import static android.util.RotationUtils.deltaRotation; +import static android.util.RotationUtils.rotateBounds; import static android.view.Display.TYPE_INTERNAL; import static android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES; import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT; @@ -1046,11 +1048,17 @@ public class DisplayPolicy { return; } - // Get displayFrames bounds - sTmpDisplayFrameBounds.set(0, 0, displayFrames.mDisplayWidth, displayFrames.mDisplayHeight); + // Get displayFrames bounds as it is on WindowState's rotation. + final int deltaRotation = deltaRotation(windowRotation, displayFrames.mRotation); + if (deltaRotation == Surface.ROTATION_90 || deltaRotation == Surface.ROTATION_270) { + sTmpDisplayFrameBounds.set( + 0, 0, displayFrames.mDisplayHeight, displayFrames.mDisplayWidth); + } else { + sTmpDisplayFrameBounds.set( + 0, 0, displayFrames.mDisplayWidth, displayFrames.mDisplayHeight); + } // Rotate the WindowState's bounds based on the displayFrames rotation - mDisplayContent.rotateBounds(sTmpDisplayFrameBounds, windowRotation, - displayFrames.mRotation, outBounds); + rotateBounds(outBounds, sTmpDisplayFrameBounds, deltaRotation); } /** diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index b106657dee99..63cb38a59349 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.util.RotationUtils.deltaRotation; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; @@ -475,7 +476,7 @@ public class DisplayRotation { "Display id=%d rotation changed to %d from %d, lastOrientation=%d", displayId, rotation, oldRotation, lastOrientation); - if (DisplayContent.deltaRotation(rotation, oldRotation) != 2) { + if (deltaRotation(oldRotation, rotation) != Surface.ROTATION_180) { mDisplayContent.mWaitingForConfig = true; } @@ -788,8 +789,13 @@ public class DisplayRotation { mFixedToUserRotation = fixedToUserRotation; mDisplayWindowSettings.setFixedToUserRotation(mDisplayContent, fixedToUserRotation); - mService.updateRotation(true /* alwaysSendConfiguration */, - false /* forceRelayout */); + if (mDisplayContent.mFocusedApp != null) { + // We record the last focused TDA that respects orientation request, check if this + // change may affect it. + mDisplayContent.onLastFocusedTaskDisplayAreaChanged( + mDisplayContent.mFocusedApp.getDisplayArea()); + } + mDisplayContent.updateOrientation(); } @VisibleForTesting diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedTaskDividerController.java index de4bdaa57efa..fb9d06441536 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedTaskDividerController.java @@ -19,16 +19,16 @@ package com.android.server.wm; import android.graphics.Rect; /** - * Keeps information about the docked stack divider. + * Keeps information about the docked task divider. */ -public class DockedStackDividerController { +public class DockedTaskDividerController { private final DisplayContent mDisplayContent; private boolean mResizing; private final Rect mTouchRegion = new Rect(); - DockedStackDividerController(DisplayContent displayContent) { + DockedTaskDividerController(DisplayContent displayContent) { mDisplayContent = displayContent; } @@ -58,6 +58,6 @@ public class DockedStackDividerController { private void resetDragResizingChangeReported() { mDisplayContent.forAllWindows(WindowState::resetDragResizingChangeReported, - true /* traverseTopToBottom */ ); + true /* traverseTopToBottom */); } } diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java index 8fe2481f9eda..15e078b478b8 100644 --- a/services/core/java/com/android/server/wm/PinnedStackController.java +++ b/services/core/java/com/android/server/wm/PinnedTaskController.java @@ -29,18 +29,18 @@ import android.util.DisplayMetrics; import android.util.Log; import android.util.Slog; import android.view.DisplayInfo; -import android.view.IPinnedStackListener; +import android.view.IPinnedTaskListener; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; /** - * Holds the common state of the pinned stack between the system and SystemUI. If SystemUI ever + * Holds the common state of the pinned task between the system and SystemUI. If SystemUI ever * needs to be restarted, it will be notified with the last known state. * - * Changes to the pinned stack also flow through this controller, and generally, the system only - * changes the pinned stack bounds through this controller in two ways: + * Changes to the pinned task also flow through this controller, and generally, the system only + * changes the pinned task bounds through this controller in two ways: * * 1) When first entering PiP: the controller returns the valid bounds given, taking aspect ratio * and IME state into account. @@ -49,18 +49,18 @@ import java.util.List; * SystemUI adjustments (ie. expanded for menu, interaction, etc). * * Other changes in the system, including adjustment of IME, configuration change, and more are - * handled by SystemUI (similar to the docked stack divider). + * handled by SystemUI (similar to the docked task divider). */ -class PinnedStackController { +class PinnedTaskController { - private static final String TAG = TAG_WITH_CLASS_NAME ? "PinnedStackController" : TAG_WM; + private static final String TAG = TAG_WITH_CLASS_NAME ? "PinnedTaskController" : TAG_WM; private final WindowManagerService mService; private final DisplayContent mDisplayContent; - private IPinnedStackListener mPinnedStackListener; - private final PinnedStackListenerDeathHandler mPinnedStackListenerDeathHandler = - new PinnedStackListenerDeathHandler(); + private IPinnedTaskListener mPinnedTaskListener; + private final PinnedTaskListenerDeathHandler mPinnedTaskListenerDeathHandler = + new PinnedTaskListenerDeathHandler(); /** Whether the PiP is entering or leaving. */ private boolean mIsPipWindowingModeChanging; @@ -72,7 +72,7 @@ class PinnedStackController { private ArrayList<RemoteAction> mActions = new ArrayList<>(); private float mAspectRatio = -1f; - // Used to calculate stack bounds across rotations + // Used to calculate task bounds across rotations private final DisplayInfo mDisplayInfo = new DisplayInfo(); // The aspect ratio bounds of the PIP. @@ -85,19 +85,19 @@ class PinnedStackController { /** * Handler for the case where the listener dies. */ - private class PinnedStackListenerDeathHandler implements IBinder.DeathRecipient { + private class PinnedTaskListenerDeathHandler implements IBinder.DeathRecipient { @Override public void binderDied() { // Clean up the state if the listener dies - if (mPinnedStackListener != null) { - mPinnedStackListener.asBinder().unlinkToDeath(mPinnedStackListenerDeathHandler, 0); + if (mPinnedTaskListener != null) { + mPinnedTaskListener.asBinder().unlinkToDeath(mPinnedTaskListenerDeathHandler, 0); } - mPinnedStackListener = null; + mPinnedTaskListener = null; } } - PinnedStackController(WindowManagerService service, DisplayContent displayContent) { + PinnedTaskController(WindowManagerService service, DisplayContent displayContent) { mService = service; mDisplayContent = displayContent; mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo()); @@ -121,17 +121,17 @@ class PinnedStackController { } /** - * Registers a pinned stack listener. + * Registers a pinned task listener. */ - void registerPinnedStackListener(IPinnedStackListener listener) { + void registerPinnedTaskListener(IPinnedTaskListener listener) { try { - listener.asBinder().linkToDeath(mPinnedStackListenerDeathHandler, 0); - mPinnedStackListener = listener; + listener.asBinder().linkToDeath(mPinnedTaskListenerDeathHandler, 0); + mPinnedTaskListener = listener; notifyImeVisibilityChanged(mIsImeShowing, mImeHeight); notifyMovementBoundsChanged(false /* fromImeAdjustment */); notifyActionsChanged(mActions); } catch (RemoteException e) { - Log.e(TAG, "Failed to register pinned stack listener", e); + Log.e(TAG, "Failed to register pinned task listener", e); } } @@ -139,8 +139,8 @@ class PinnedStackController { * @return whether the given {@param aspectRatio} is valid. */ public boolean isValidPictureInPictureAspectRatio(float aspectRatio) { - return Float.compare(mMinAspectRatio, aspectRatio) <= 0 && - Float.compare(aspectRatio, mMaxAspectRatio) <= 0; + return Float.compare(mMinAspectRatio, aspectRatio) <= 0 + && Float.compare(aspectRatio, mMaxAspectRatio) <= 0; } /** Returns {@code true} if the PiP is on screen or is changing windowing mode. */ @@ -152,7 +152,7 @@ class PinnedStackController { return pinnedTask != null && pinnedTask.hasChild(); } - /** Sets whether a visible stack is changing from or to pinned mode. */ + /** Sets whether a visible task is changing from or to pinned mode. */ void setPipWindowingModeChanging(boolean isPipWindowingModeChanging) { mIsPipWindowingModeChanging = isPipWindowingModeChanging; } @@ -162,9 +162,9 @@ class PinnedStackController { * so that the default bounds will be returned for the next session. */ void onActivityHidden(ComponentName componentName) { - if (mPinnedStackListener == null) return; + if (mPinnedTaskListener == null) return; try { - mPinnedStackListener.onActivityHidden(componentName); + mPinnedTaskListener.onActivityHidden(componentName); } catch (RemoteException e) { Slog.e(TAG_WM, "Error delivering reset reentry fraction event.", e); } @@ -223,9 +223,9 @@ class PinnedStackController { * Notifies listeners that the PIP needs to be adjusted for the IME. */ private void notifyImeVisibilityChanged(boolean imeVisible, int imeHeight) { - if (mPinnedStackListener != null) { + if (mPinnedTaskListener != null) { try { - mPinnedStackListener.onImeVisibilityChanged(imeVisible, imeHeight); + mPinnedTaskListener.onImeVisibilityChanged(imeVisible, imeHeight); } catch (RemoteException e) { Slog.e(TAG_WM, "Error delivering bounds changed event.", e); } @@ -233,9 +233,9 @@ class PinnedStackController { } private void notifyAspectRatioChanged(float aspectRatio) { - if (mPinnedStackListener == null) return; + if (mPinnedTaskListener == null) return; try { - mPinnedStackListener.onAspectRatioChanged(aspectRatio); + mPinnedTaskListener.onAspectRatioChanged(aspectRatio); } catch (RemoteException e) { Slog.e(TAG_WM, "Error delivering aspect ratio changed event.", e); } @@ -245,9 +245,9 @@ class PinnedStackController { * Notifies listeners that the PIP actions have changed. */ private void notifyActionsChanged(List<RemoteAction> actions) { - if (mPinnedStackListener != null) { + if (mPinnedTaskListener != null) { try { - mPinnedStackListener.onActionsChanged(new ParceledListSlice(actions)); + mPinnedTaskListener.onActionsChanged(new ParceledListSlice(actions)); } catch (RemoteException e) { Slog.e(TAG_WM, "Error delivering actions changed event.", e); } @@ -259,11 +259,11 @@ class PinnedStackController { */ private void notifyMovementBoundsChanged(boolean fromImeAdjustment) { synchronized (mService.mGlobalLock) { - if (mPinnedStackListener == null) { + if (mPinnedTaskListener == null) { return; } try { - mPinnedStackListener.onMovementBoundsChanged(fromImeAdjustment); + mPinnedTaskListener.onMovementBoundsChanged(fromImeAdjustment); } catch (RemoteException e) { Slog.e(TAG_WM, "Error delivering actions changed event.", e); } @@ -271,7 +271,7 @@ class PinnedStackController { } void dump(String prefix, PrintWriter pw) { - pw.println(prefix + "PinnedStackController"); + pw.println(prefix + "PinnedTaskController"); pw.println(prefix + " mIsImeShowing=" + mIsImeShowing); pw.println(prefix + " mImeHeight=" + mImeHeight); pw.println(prefix + " mAspectRatio=" + mAspectRatio); diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index 533c82e599c9..95a4f69edd57 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; +import static android.util.RotationUtils.deltaRotation; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC; @@ -174,7 +175,7 @@ class ScreenRotationAnimation { // apply rotation animation because there should be a top app shown as rotated. So the // specified original rotation customizes the direction of animation to have better look // when restoring the rotated app to the same rotation as current display. - final int delta = DisplayContent.deltaRotation(originalRotation, realOriginalRotation); + final int delta = deltaRotation(originalRotation, realOriginalRotation); final boolean flipped = delta == Surface.ROTATION_90 || delta == Surface.ROTATION_270; mOriginalWidth = flipped ? originalHeight : originalWidth; mOriginalHeight = flipped ? originalWidth : originalHeight; @@ -330,7 +331,7 @@ class ScreenRotationAnimation { // Compute the transformation matrix that must be applied // to the snapshot to make it stay in the same original position // with the current screen rotation. - int delta = DisplayContent.deltaRotation(rotation, Surface.ROTATION_0); + int delta = deltaRotation(rotation, Surface.ROTATION_0); RotationAnimationUtils.createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix); setRotationTransform(t, mSnapshotInitialMatrix); @@ -352,8 +353,7 @@ class ScreenRotationAnimation { mStarted = true; // Figure out how the screen has moved from the original rotation. - int delta = DisplayContent.deltaRotation(mCurRotation, mOriginalRotation); - + int delta = deltaRotation(mCurRotation, mOriginalRotation); final boolean customAnim; if (exitAnim != 0 && enterAnim != 0) { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index d60b6e0ef81d..29c7ff118595 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -2267,7 +2267,7 @@ class Task extends WindowContainer<WindowContainer> { } if (pipChanging) { - mDisplayContent.getPinnedStackController().setPipWindowingModeChanging(true); + mDisplayContent.getPinnedTaskController().setPipWindowingModeChanging(true); // If the top activity is using fixed rotation, it should be changing from PiP to // fullscreen with display orientation change. Do not notify fullscreen task organizer // because the restoration of task surface and the transformation of activity surface @@ -2297,7 +2297,7 @@ class Task extends WindowContainer<WindowContainer> { } } finally { if (pipChanging) { - mDisplayContent.getPinnedStackController().setPipWindowingModeChanging(false); + mDisplayContent.getPinnedTaskController().setPipWindowingModeChanging(false); } } @@ -2361,9 +2361,7 @@ class Task extends WindowContainer<WindowContainer> { final int newRotation = getWindowConfiguration().getRotation(); final boolean rotationChanged = prevRotation != newRotation; if (rotationChanged) { - mDisplayContent.rotateBounds( - newParentConfig.windowConfiguration.getBounds(), prevRotation, newRotation, - newBounds); + mDisplayContent.rotateBounds(prevRotation, newRotation, newBounds); setBounds(newBounds); } } @@ -7792,18 +7790,18 @@ class Task extends WindowContainer<WindowContainer> { return; } - final PinnedStackController pinnedStackController = - getDisplayContent().getPinnedStackController(); + final PinnedTaskController pinnedTaskController = + getDisplayContent().getPinnedTaskController(); - if (Float.compare(aspectRatio, pinnedStackController.getAspectRatio()) == 0) { + if (Float.compare(aspectRatio, pinnedTaskController.getAspectRatio()) == 0) { return; } // Notify the pinned stack controller about aspect ratio change. // This would result a callback delivered from SystemUI to WM to start animation, // if the bounds are ought to be altered due to aspect ratio change. - pinnedStackController.setAspectRatio( - pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio) + pinnedTaskController.setAspectRatio( + pinnedTaskController.isValidPictureInPictureAspectRatio(aspectRatio) ? aspectRatio : -1f); } @@ -7819,7 +7817,7 @@ class Task extends WindowContainer<WindowContainer> { return; } - getDisplayContent().getPinnedStackController().setActions(actions); + getDisplayContent().getPinnedTaskController().setActions(actions); } /** Returns true if a removal action is still being deferred. */ diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java index 9b7257057836..04ec4bd83511 100644 --- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java @@ -130,8 +130,8 @@ class TaskChangeNotificationController { l.onActivityForcedResizable((String) m.obj, m.arg1, m.arg2); }; - private final TaskStackConsumer mNotifyActivityDismissingDockedStack = (l, m) -> { - l.onActivityDismissingDockedStack(); + private final TaskStackConsumer mNotifyActivityDismissingDockedTask = (l, m) -> { + l.onActivityDismissingDockedTask(); }; private final TaskStackConsumer mNotifyActivityLaunchOnSecondaryDisplayFailed = (l, m) -> { @@ -235,7 +235,7 @@ class TaskChangeNotificationController { forAllRemoteListeners(mNotifyActivityForcedResizable, msg); break; case NOTIFY_ACTIVITY_DISMISSING_DOCKED_ROOT_TASK_MSG: - forAllRemoteListeners(mNotifyActivityDismissingDockedStack, msg); + forAllRemoteListeners(mNotifyActivityDismissingDockedTask, msg); break; case NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG: forAllRemoteListeners(mNotifyActivityLaunchOnSecondaryDisplayFailed, msg); @@ -391,7 +391,7 @@ class TaskChangeNotificationController { void notifyActivityDismissingDockedRootTask() { mHandler.removeMessages(NOTIFY_ACTIVITY_DISMISSING_DOCKED_ROOT_TASK_MSG); final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_ROOT_TASK_MSG); - forAllLocalListeners(mNotifyActivityDismissingDockedStack, msg); + forAllLocalListeners(mNotifyActivityDismissingDockedTask, msg); msg.sendToTarget(); } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index c0bce6be8c54..1531e56bf490 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -596,9 +596,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { try { synchronized (mGlobalLock) { final WindowContainer wc = WindowContainer.fromBinder(token.asBinder()); - if (wc == null) { - throw new IllegalArgumentException("Can't resolve window from token"); - } + if (wc == null) return false; final Task task = wc.asTask(); if (task == null) return false; if (!task.mCreatedByOrganizer) { diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java index 361694b325f9..9245f8c3efe5 100644 --- a/services/core/java/com/android/server/wm/WindowFrames.java +++ b/services/core/java/com/android/server/wm/WindowFrames.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static com.android.server.wm.WindowFramesProto.COMPAT_FRAME; import static com.android.server.wm.WindowFramesProto.CONTAINING_FRAME; import static com.android.server.wm.WindowFramesProto.DISPLAY_FRAME; import static com.android.server.wm.WindowFramesProto.FRAME; @@ -177,7 +178,7 @@ public class WindowFrames { mDisplayFrame.dumpDebug(proto, DISPLAY_FRAME); mContainingFrame.dumpDebug(proto, CONTAINING_FRAME); mFrame.dumpDebug(proto, FRAME); - + mCompatFrame.dumpDebug(proto, COMPAT_FRAME); proto.end(token); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 89d30408fe25..71d2b2f7b882 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -230,7 +230,7 @@ import android.view.IDisplayWindowListener; import android.view.IDisplayWindowRotationController; import android.view.IInputFilter; import android.view.IOnKeyguardExitResult; -import android.view.IPinnedStackListener; +import android.view.IPinnedTaskListener; import android.view.IRecentsAnimationRunner; import android.view.IRotationWatcher; import android.view.IScrollCaptureCallbacks; @@ -2981,7 +2981,7 @@ public class WindowManagerService extends IWindowManager.Stub } boolean isValidPictureInPictureAspectRatio(DisplayContent displayContent, float aspectRatio) { - return displayContent.getPinnedStackController().isValidPictureInPictureAspectRatio( + return displayContent.getPinnedTaskController().isValidPictureInPictureAspectRatio( aspectRatio); } @@ -6880,7 +6880,7 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void setDockedStackDividerTouchRegion(Rect touchRegion) { + public void setDockedTaskDividerTouchRegion(Rect touchRegion) { synchronized (mGlobalLock) { final DisplayContent dc = getDefaultDisplayContentLocked(); dc.getDockedDividerController().setTouchRegion(touchRegion); @@ -6905,10 +6905,9 @@ public class WindowManagerService extends IWindowManager.Stub return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics); } - @Override - public void registerPinnedStackListener(int displayId, IPinnedStackListener listener) { + public void registerPinnedTaskListener(int displayId, IPinnedTaskListener listener) { if (!checkCallingPermission(REGISTER_WINDOW_MANAGER_LISTENERS, - "registerPinnedStackListener()")) { + "registerPinnedTaskListener()")) { return; } if (!mAtmService.mSupportsPictureInPicture) { @@ -6916,7 +6915,7 @@ public class WindowManagerService extends IWindowManager.Stub } synchronized (mGlobalLock) { final DisplayContent displayContent = mRoot.getDisplayContent(displayId); - displayContent.getPinnedStackController().registerPinnedStackListener(listener); + displayContent.getPinnedTaskController().registerPinnedTaskListener(listener); } } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 63a083261614..9973664346f0 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -17,7 +17,6 @@ package com.android.server.wm; import static android.Manifest.permission.READ_FRAME_BUFFER; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT; @@ -426,8 +425,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } if (windowingMode > -1) { - if (mService.isInLockTaskMode() && windowingMode != WINDOWING_MODE_FULLSCREEN) { - throw new UnsupportedOperationException("Not supported to set non-fullscreen" + if (mService.isInLockTaskMode() + && WindowConfiguration.inMultiWindowMode(windowingMode)) { + throw new UnsupportedOperationException("Not supported to set multi-window" + " windowing mode during locked task mode."); } container.setWindowingMode(windowingMode); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 3dfdedfec0d1..9ae5beb625f8 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -169,7 +169,9 @@ import static com.android.server.wm.WindowStateProto.DISPLAY_ID; import static com.android.server.wm.WindowStateProto.FINISHED_SEAMLESS_ROTATION_FRAME; import static com.android.server.wm.WindowStateProto.FORCE_SEAMLESS_ROTATION; import static com.android.server.wm.WindowStateProto.GIVEN_CONTENT_INSETS; +import static com.android.server.wm.WindowStateProto.GLOBAL_SCALE; import static com.android.server.wm.WindowStateProto.HAS_SURFACE; +import static com.android.server.wm.WindowStateProto.IN_SIZE_COMPAT_MODE; import static com.android.server.wm.WindowStateProto.IS_ON_SCREEN; import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY; import static com.android.server.wm.WindowStateProto.IS_VISIBLE; @@ -1222,7 +1224,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // But in docked we want to behave like fullscreen and behave as if the task // were given smaller bounds for the purposes of layout. Skip adjustments for // the root pinned task, they are handled separately in the - // PinnedStackController. + // PinnedTaskController. windowFrames.mContainingFrame.bottom = windowFrames.mParentFrame.bottom; } } @@ -4002,6 +4004,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP proto.write(PENDING_SEAMLESS_ROTATION, mPendingSeamlessRotate != null); proto.write(FINISHED_SEAMLESS_ROTATION_FRAME, mFinishSeamlessRotateFrameNumber); proto.write(FORCE_SEAMLESS_ROTATION, mForceSeamlesslyRotate); + proto.write(IN_SIZE_COMPAT_MODE, inSizeCompatMode()); + proto.write(GLOBAL_SCALE, mGlobalScale); proto.end(token); } diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp index 4551d49d9e58..f054e7c73015 100644 --- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp +++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp @@ -86,7 +86,7 @@ static int compactMemory(const std::vector<Vma>& vmas, int pid, int madviseType) int pidfd = syscall(__NR_pidfd_open, pid, 0); err = -errno; - if (err < 0) { + if (pidfd < 0) { // Skip compaction if failed to open pidfd with any error return err; } diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 840e119de477..10705af9ac38 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -158,6 +158,20 @@ static struct { static struct { jclass clazz; jmethodID constructor; + jfieldID lightTypeSingle; + jfieldID lightTypePlayerId; + jfieldID lightTypeRgb; +} gLightClassInfo; + +static struct { + jclass clazz; + jmethodID constructor; + jmethodID add; +} gArrayListClassInfo; + +static struct { + jclass clazz; + jmethodID constructor; jmethodID keyAt; jmethodID valueAt; jmethodID size; @@ -1923,6 +1937,79 @@ static jintArray nativeGetVibratorIds(JNIEnv* env, jclass clazz, jlong ptr, jint return vibIdArray; } +static jobject nativeGetLights(JNIEnv* env, jclass clazz, jlong ptr, jint deviceId) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + jobject jLights = env->NewObject(gArrayListClassInfo.clazz, gArrayListClassInfo.constructor); + + std::vector<int> lightIds = im->getInputManager()->getReader()->getLightIds(deviceId); + + for (size_t i = 0; i < lightIds.size(); i++) { + const InputDeviceLightInfo* lightInfo = + im->getInputManager()->getReader()->getLightInfo(deviceId, lightIds[i]); + if (lightInfo == nullptr) { + ALOGW("Failed to get input device %d light info for id %d", deviceId, lightIds[i]); + continue; + } + + jint jTypeId = 0; + if (lightInfo->type == InputDeviceLightType::SINGLE) { + jTypeId = + env->GetStaticIntField(gLightClassInfo.clazz, gLightClassInfo.lightTypeSingle); + } else if (lightInfo->type == InputDeviceLightType::PLAYER_ID) { + jTypeId = env->GetStaticIntField(gLightClassInfo.clazz, + gLightClassInfo.lightTypePlayerId); + } else if (lightInfo->type == InputDeviceLightType::RGB || + lightInfo->type == InputDeviceLightType::MULTI_COLOR) { + jTypeId = env->GetStaticIntField(gLightClassInfo.clazz, gLightClassInfo.lightTypeRgb); + } else { + ALOGW("Unknown light type %d", lightInfo->type); + continue; + } + ScopedLocalRef<jobject> + lightObj(env, + env->NewObject(gLightClassInfo.clazz, gLightClassInfo.constructor, + (jint)lightInfo->id, (jint)lightInfo->ordinal, jTypeId, + env->NewStringUTF(lightInfo->name.c_str()))); + // Add light object to list + env->CallBooleanMethod(jLights, gArrayListClassInfo.add, lightObj.get()); + } + + return jLights; +} + +static jint nativeGetLightPlayerId(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId, + jint lightId) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + std::optional<int32_t> ret = + im->getInputManager()->getReader()->getLightPlayerId(deviceId, lightId); + + return static_cast<jint>(ret.value_or(0)); +} + +static jint nativeGetLightColor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId, + jint lightId) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + std::optional<int32_t> ret = + im->getInputManager()->getReader()->getLightColor(deviceId, lightId); + return static_cast<jint>(ret.value_or(0)); +} + +static void nativeSetLightPlayerId(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId, + jint lightId, jint playerId) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + im->getInputManager()->getReader()->setLightPlayerId(deviceId, lightId, playerId); +} + +static void nativeSetLightColor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId, + jint lightId, jint color) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + im->getInputManager()->getReader()->setLightColor(deviceId, lightId, color); +} + static jint nativeGetBatteryCapacity(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); @@ -2192,6 +2279,11 @@ static const JNINativeMethod gInputManagerMethods[] = { {"nativeCancelVibrate", "(JII)V", (void*)nativeCancelVibrate}, {"nativeIsVibrating", "(JI)Z", (void*)nativeIsVibrating}, {"nativeGetVibratorIds", "(JI)[I", (void*)nativeGetVibratorIds}, + {"nativeGetLights", "(JI)Ljava/util/List;", (void*)nativeGetLights}, + {"nativeGetLightPlayerId", "(JII)I", (void*)nativeGetLightPlayerId}, + {"nativeGetLightColor", "(JII)I", (void*)nativeGetLightColor}, + {"nativeSetLightPlayerId", "(JIII)V", (void*)nativeSetLightPlayerId}, + {"nativeSetLightColor", "(JIII)V", (void*)nativeSetLightColor}, {"nativeGetBatteryCapacity", "(JI)I", (void*)nativeGetBatteryCapacity}, {"nativeGetBatteryStatus", "(JI)I", (void*)nativeGetBatteryStatus}, {"nativeReloadKeyboardLayouts", "(J)V", (void*)nativeReloadKeyboardLayouts}, @@ -2386,6 +2478,27 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gTouchCalibrationClassInfo.getAffineTransform, gTouchCalibrationClassInfo.clazz, "getAffineTransform", "()[F"); + // Light + FIND_CLASS(gLightClassInfo.clazz, "android/hardware/lights/Light"); + gLightClassInfo.clazz = jclass(env->NewGlobalRef(gLightClassInfo.clazz)); + GET_METHOD_ID(gLightClassInfo.constructor, gLightClassInfo.clazz, "<init>", + "(IIILjava/lang/String;)V"); + + gLightClassInfo.clazz = jclass(env->NewGlobalRef(gLightClassInfo.clazz)); + gLightClassInfo.lightTypeSingle = + env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_INPUT_SINGLE", "I"); + gLightClassInfo.lightTypePlayerId = + env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_INPUT_PLAYER_ID", "I"); + gLightClassInfo.lightTypeRgb = + env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_INPUT_RGB", "I"); + + // ArrayList + FIND_CLASS(gArrayListClassInfo.clazz, "java/util/ArrayList"); + gArrayListClassInfo.clazz = jclass(env->NewGlobalRef(gArrayListClassInfo.clazz)); + GET_METHOD_ID(gArrayListClassInfo.constructor, gArrayListClassInfo.clazz, "<init>", "()V"); + GET_METHOD_ID(gArrayListClassInfo.add, gArrayListClassInfo.clazz, "add", + "(Ljava/lang/Object;)Z"); + // SparseArray FIND_CLASS(gSparseArrayClassInfo.clazz, "android/util/SparseArray"); gSparseArrayClassInfo.clazz = jclass(env->NewGlobalRef(gSparseArrayClassInfo.clazz)); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index b52347f509f8..ef7afc8d1894 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -129,6 +129,15 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) { } + @Override + public void setDeviceOwnerType(@NonNull ComponentName admin, int deviceOwnerType) { + } + + @Override + public int getDeviceOwnerType(@NonNull ComponentName admin) { + return 0; + } + public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {} public boolean canAdminGrantSensorsPermissionsForUser(int userId) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 7260732bbcb6..524892b2ec4b 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -156,6 +156,7 @@ import android.app.admin.DeviceAdminReceiver; import android.app.admin.DevicePolicyCache; import android.app.admin.DevicePolicyEventLogger; import android.app.admin.DevicePolicyManager; +import android.app.admin.DevicePolicyManager.DeviceOwnerType; import android.app.admin.DevicePolicyManager.DevicePolicyOperation; import android.app.admin.DevicePolicyManager.OperationSafetyReason; import android.app.admin.DevicePolicyManager.PasswordComplexity; @@ -333,6 +334,7 @@ import com.google.android.collect.Sets; import org.xmlpull.v1.XmlPullParserException; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -340,6 +342,9 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.Constructor; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.text.DateFormat; import java.time.LocalDate; import java.util.ArrayList; @@ -3399,8 +3404,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } Objects.requireNonNull(adminReceiver, "ComponentName is null"); - Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()), - "Non-shell user attempted to call forceRemoveActiveAdmin"); + Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()) + || hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS), + "Caller must be shell or hold MANAGE_PROFILE_AND_DEVICE_OWNERS to call " + + "forceRemoveActiveAdmin"); mInjector.binderWithCleanCallingIdentity(() -> { synchronized (getLockObject()) { if (!isAdminTestOnlyLocked(adminReceiver, userHandle)) { @@ -5622,7 +5629,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Get attestation flags, if any. final int[] attestationUtilsFlags = translateIdAttestationFlags(idAttestationFlags); final boolean deviceIdAttestationRequired = attestationUtilsFlags != null; - final KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec(); + KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec(); final String alias = keySpec.getKeystoreAlias(); Preconditions.checkStringNotEmpty(alias, "Empty alias provided"); @@ -5643,6 +5650,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp))); } + if (TextUtils.isEmpty(alias)) { + throw new IllegalArgumentException("Empty alias provided."); + } // As the caller will be granted access to the key, ensure no UID was specified, as // it will not have the desired effect. if (keySpec.getUid() != KeyStore.UID_SELF) { @@ -5651,19 +5661,26 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } + if (deviceIdAttestationRequired) { + if (keySpec.getAttestationChallenge() == null) { + throw new IllegalArgumentException( + "Requested Device ID attestation but challenge is empty."); + } + KeyGenParameterSpec.Builder specBuilder = new KeyGenParameterSpec.Builder(keySpec); + specBuilder.setAttestationIds(attestationUtilsFlags); + specBuilder.setDevicePropertiesAttestationIncluded(true); + keySpec = specBuilder.build(); + } + + final UserHandle userHandle = mInjector.binderGetCallingUserHandle(); final long id = mInjector.binderClearCallingIdentity(); try { try (KeyChainConnection keyChainConnection = - KeyChain.bindAsUser(mContext, caller.getUserHandle())) { + KeyChain.bindAsUser(mContext, userHandle)) { IKeyChainService keyChain = keyChainConnection.getService(); - // Copy the provided keySpec, excluding the attestation challenge, which will be - // used later for requesting key attestation record. - final KeyGenParameterSpec noAttestationSpec = new KeyGenParameterSpec.Builder( - keySpec).setAttestationChallenge(null).build(); - final int generationResult = keyChain.generateKeyPair(algorithm, - new ParcelableKeyGenParameterSpec(noAttestationSpec)); + new ParcelableKeyGenParameterSpec(keySpec)); if (generationResult != KeyChain.KEY_GEN_SUCCESS) { Log.e(LOG_TAG, String.format( "KeyChain failed to generate a keypair, error %d.", generationResult)); @@ -5672,6 +5689,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { throw new ServiceSpecificException( DevicePolicyManager.KEY_GEN_STRONGBOX_UNAVAILABLE, String.format("KeyChain error: %d", generationResult)); + case KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS: + throw new UnsupportedOperationException( + "Device does not support Device ID attestation."); default: logGenerateKeyPairFailure(caller, isCredentialManagementApp); return false; @@ -5685,23 +5705,27 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // that UID. keyChain.setGrant(caller.getUid(), alias, true); - final byte[] attestationChallenge = keySpec.getAttestationChallenge(); - if (attestationChallenge != null) { - final int attestationResult = keyChain.attestKey( - alias, attestationChallenge, attestationUtilsFlags, attestationChain); - if (attestationResult != KeyChain.KEY_ATTESTATION_SUCCESS) { - Log.e(LOG_TAG, String.format( - "Attestation for %s failed (rc=%d), deleting key.", - alias, attestationResult)); - keyChain.removeKeyPair(alias); - if (attestationResult == KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS) { - throw new UnsupportedOperationException( - "Device does not support Device ID attestation."); + try { + final List<byte[]> encodedCerts = new ArrayList(); + final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + final byte[] certChainBytes = keyChain.getCaCertificates(alias); + encodedCerts.add(keyChain.getCertificate(alias)); + if (certChainBytes != null) { + final Collection<X509Certificate> certs = + (Collection<X509Certificate>) certFactory.generateCertificates( + new ByteArrayInputStream(certChainBytes)); + for (X509Certificate cert : certs) { + encodedCerts.add(cert.getEncoded()); } - logGenerateKeyPairFailure(caller, isCredentialManagementApp); - return false; } + + attestationChain.shallowCopyFrom(new KeymasterCertificateChain(encodedCerts)); + } catch (CertificateException e) { + logGenerateKeyPairFailure(caller, isCredentialManagementApp); + Log.e(LOG_TAG, "While retrieving certificate chain.", e); + return false; } + DevicePolicyEventLogger .createEvent(DevicePolicyEnums.GENERATE_KEY_PAIR) .setAdmin(caller.getPackageName()) @@ -7560,8 +7584,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } private void sendProfileOwnerCommand(String action, Bundle extras, @UserIdInt int userId) { - sendActiveAdminCommand(action, extras, userId, - mOwners.getProfileOwnerComponent(userId)); + sendActiveAdminCommand(action, extras, userId, mOwners.getProfileOwnerComponent(userId)); } private void sendActiveAdminCommand(String action, Bundle extras, @@ -8089,7 +8112,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } if (!callingUserOnly) { - Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity())); + Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()) + || hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)); } synchronized (getLockObject()) { if (!mOwners.hasDeviceOwner()) { @@ -12351,8 +12375,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { extras.putInt(DeviceAdminReceiver.EXTRA_OPERATION_SAFETY_REASON, reason); extras.putBoolean(DeviceAdminReceiver.EXTRA_OPERATION_SAFETY_STATE, isSafe); - sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_OPERATION_SAFETY_STATE_CHANGED, - extras); + if (mOwners.hasDeviceOwner()) { + sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_OPERATION_SAFETY_STATE_CHANGED, + extras); + } for (int profileOwnerId : mOwners.getProfileOwnerKeys()) { sendProfileOwnerCommand(DeviceAdminReceiver.ACTION_OPERATION_SAFETY_STATE_CHANGED, extras, profileOwnerId); @@ -12543,8 +12569,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void clearSystemUpdatePolicyFreezePeriodRecord() { - Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()), - "Non-shell user attempted to call clearSystemUpdatePolicyFreezePeriodRecord"); + Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()) + || hasCallingOrSelfPermission(permission.CLEAR_FREEZE_PERIOD), + "Caller must be shell, or hold CLEAR_FREEZE_PERIOD permission to call " + + "clearSystemUpdatePolicyFreezePeriodRecord"); synchronized (getLockObject()) { // Print out current record to help diagnosed CTS failures Slog.i(LOG_TAG, "Clear freeze period record: " @@ -13487,7 +13515,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(); // Only adb or system apps with the right permission can mark a profile owner on // organization-owned device. - if (!(isAdb(caller) || hasCallingPermission(permission.MARK_DEVICE_ORGANIZATION_OWNED))) { + if (!(isAdb(caller) || hasCallingPermission(permission.MARK_DEVICE_ORGANIZATION_OWNED) + || hasCallingPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS))) { throw new SecurityException( "Only the system can mark a profile owner of organization-owned device."); } @@ -13806,8 +13835,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public long forceSecurityLogs() { - Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()), - "Non-shell user attempted to call forceSecurityLogs"); + Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()) + || hasCallingOrSelfPermission(permission.FORCE_DEVICE_POLICY_MANAGER_LOGS), + "Caller must be shell or hold FORCE_DEVICE_POLICY_MANAGER_LOGS to call " + + "forceSecurityLogs"); if (!mInjector.securityLogGetLoggingEnabledProperty()) { throw new IllegalStateException("logging is not available"); } @@ -14327,8 +14358,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public long forceNetworkLogs() { - Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()), - "Non-shell user attempted to call forceNetworkLogs"); + Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()) + || hasCallingOrSelfPermission(permission.FORCE_DEVICE_POLICY_MANAGER_LOGS), + "Caller must be shell or hold FORCE_DEVICE_POLICY_MANAGER_LOGS to call " + + "forceNetworkLogs"); synchronized (getLockObject()) { if (!isNetworkLoggingEnabledInternalLocked()) { throw new IllegalStateException("logging is not available"); @@ -16731,6 +16764,37 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override + public void setDeviceOwnerType(@NonNull ComponentName admin, + @DeviceOwnerType int deviceOwnerType) { + Preconditions.checkCallAuthorization(hasCallingOrSelfPermission( + permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)); + verifyDeviceOwnerTypePreconditions(admin); + + final String packageName = admin.getPackageName(); + Preconditions.checkState(!mOwners.isDeviceOwnerTypeSetForDeviceOwner(packageName), + "The device owner type has already been set for " + packageName); + + synchronized (getLockObject()) { + mOwners.setDeviceOwnerType(packageName, deviceOwnerType); + } + } + + @Override + @DeviceOwnerType + public int getDeviceOwnerType(@NonNull ComponentName admin) { + verifyDeviceOwnerTypePreconditions(admin); + synchronized (getLockObject()) { + return mOwners.getDeviceOwnerType(admin.getPackageName()); + } + } + + private void verifyDeviceOwnerTypePreconditions(@NonNull ComponentName admin) { + Preconditions.checkState(mOwners.hasDeviceOwner(), "there is no device owner"); + Preconditions.checkState(mOwners.getDeviceOwnerComponent().equals(admin), + "admin is not the device owner"); + } + + @Override public void setUsbDataSignalingEnabled(String packageName, boolean enabled) { Objects.requireNonNull(packageName, "Admin package name must be provided"); final CallerIdentity caller = getCallerIdentity(packageName); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java index 776b44445678..257fc640f93c 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java @@ -23,6 +23,8 @@ import android.app.admin.DevicePolicyManager.DevicePolicyOperation; import android.app.admin.DevicePolicyManager.OperationSafetyReason; import android.app.admin.DevicePolicyManagerInternal; import android.app.admin.DevicePolicySafetyChecker; +import android.os.Handler; +import android.os.Looper; import android.util.Slog; import com.android.internal.os.IResultReceiver; @@ -42,10 +44,15 @@ final class OneTimeSafetyChecker implements DevicePolicySafetyChecker { private static final String TAG = OneTimeSafetyChecker.class.getSimpleName(); + private static final long SELF_DESTRUCT_TIMEOUT_MS = 10_000; + private final DevicePolicyManagerService mService; private final DevicePolicySafetyChecker mRealSafetyChecker; private final @DevicePolicyOperation int mOperation; private final @OperationSafetyReason int mReason; + private final Handler mHandler = new Handler(Looper.getMainLooper()); + + private boolean mDone; OneTimeSafetyChecker(DevicePolicyManagerService service, @DevicePolicyOperation int operation, @OperationSafetyReason int reason) { @@ -53,7 +60,11 @@ final class OneTimeSafetyChecker implements DevicePolicySafetyChecker { mOperation = operation; mReason = reason; mRealSafetyChecker = service.getDevicePolicySafetyChecker(); - Slog.i(TAG, "Saving real DevicePolicySafetyChecker as " + mRealSafetyChecker); + Slog.i(TAG, "OneTimeSafetyChecker constructor: operation= " + operationToString(operation) + + ", reason=" + operationSafetyReasonToString(reason) + + ", realChecker=" + mRealSafetyChecker + + ", maxDuration=" + SELF_DESTRUCT_TIMEOUT_MS + "ms"); + mHandler.postDelayed(() -> selfDestruct(), SELF_DESTRUCT_TIMEOUT_MS); } @Override @@ -99,8 +110,21 @@ final class OneTimeSafetyChecker implements DevicePolicySafetyChecker { } private void disableSelf() { + if (mDone) { + Slog.w(TAG, "disableSelf(): already disabled"); + return; + } Slog.i(TAG, "restoring DevicePolicySafetyChecker to " + mRealSafetyChecker); mService.setDevicePolicySafetyCheckerUnchecked(mRealSafetyChecker); + mDone = true; + } + + private void selfDestruct() { + if (mDone) return; + + // Usually happens when a CTS failed before calling the DPM method that would clear it + Slog.e(TAG, "Self destructing " + this + ", as it was not automatically disabled"); + disableSelf(); } @Override diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index 1e70d59a5fd5..7fdd6eeef642 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -16,10 +16,13 @@ package com.android.server.devicepolicy; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT; + import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManagerInternal; import android.app.AppOpsManagerInternal; +import android.app.admin.DevicePolicyManager.DeviceOwnerType; import android.app.admin.SystemUpdateInfo; import android.app.admin.SystemUpdatePolicy; import android.content.ComponentName; @@ -93,6 +96,7 @@ class Owners { private static final String TAG_PROFILE_OWNER = "profile-owner"; // Holds "context" for device-owner, this must not be show up before device-owner. private static final String TAG_DEVICE_OWNER_CONTEXT = "device-owner-context"; + private static final String TAG_DEVICE_OWNER_TYPE = "device-owner-type"; private static final String ATTR_NAME = "name"; private static final String ATTR_PACKAGE = "package"; @@ -109,6 +113,7 @@ class Owners { // New attribute for profile owner of organization-owned device. private static final String ATTR_PROFILE_OWNER_OF_ORG_OWNED_DEVICE = "isPoOrganizationOwnedDevice"; + private static final String ATTR_DEVICE_OWNER_TYPE_VALUE = "value"; private final UserManager mUserManager; private final UserManagerInternal mUserManagerInternal; @@ -121,6 +126,9 @@ class Owners { // Internal state for the device owner package. private OwnerInfo mDeviceOwner; + // Device owner type for a managed device. + private final ArrayMap<String, Integer> mDeviceOwnerTypes = new ArrayMap<>(); + private int mDeviceOwnerUserId = UserHandle.USER_NULL; // Internal state for the profile owner packages. @@ -334,6 +342,7 @@ class Owners { void clearDeviceOwner() { synchronized (mLock) { + mDeviceOwnerTypes.remove(mDeviceOwner.packageName); mDeviceOwner = null; mDeviceOwnerUserId = UserHandle.USER_NULL; @@ -384,12 +393,16 @@ class Owners { void transferDeviceOwnership(ComponentName target) { synchronized (mLock) { + Integer previousDeviceOwnerType = mDeviceOwnerTypes.remove(mDeviceOwner.packageName); // We don't set a name because it's not used anyway. // See DevicePolicyManagerService#getDeviceOwnerName mDeviceOwner = new OwnerInfo(null, target, mDeviceOwner.userRestrictionsMigrated, mDeviceOwner.remoteBugreportUri, mDeviceOwner.remoteBugreportHash, /* isOrganizationOwnedDevice =*/ mDeviceOwner.isOrganizationOwnedDevice); + if (previousDeviceOwnerType != null) { + mDeviceOwnerTypes.put(mDeviceOwner.packageName, previousDeviceOwnerType); + } pushToPackageManagerLocked(); pushToActivityTaskManagerLocked(); pushToActivityManagerLocked(); @@ -596,6 +609,37 @@ class Owners { } } + void setDeviceOwnerType(String packageName, @DeviceOwnerType int deviceOwnerType) { + synchronized (mLock) { + if (!hasDeviceOwner()) { + Slog.e(TAG, "Attempting to set a device owner type when there is no device owner"); + return; + } else if (isDeviceOwnerTypeSetForDeviceOwner(packageName)) { + Slog.e(TAG, "Device owner type for " + packageName + " has already been set"); + return; + } + + mDeviceOwnerTypes.put(packageName, deviceOwnerType); + writeDeviceOwner(); + } + } + + @DeviceOwnerType + int getDeviceOwnerType(String packageName) { + synchronized (mLock) { + if (isDeviceOwnerTypeSetForDeviceOwner(packageName)) { + return mDeviceOwnerTypes.get(packageName); + } + return DEVICE_OWNER_TYPE_DEFAULT; + } + } + + boolean isDeviceOwnerTypeSetForDeviceOwner(String packageName) { + synchronized (mLock) { + return !mDeviceOwnerTypes.isEmpty() && mDeviceOwnerTypes.containsKey(packageName); + } + } + private boolean readLegacyOwnerFileLocked(File file) { if (!file.exists()) { // Already migrated or the device has no owners. @@ -880,6 +924,16 @@ class Owners { out.startTag(null, TAG_DEVICE_OWNER_CONTEXT); out.attributeInt(null, ATTR_USERID, mDeviceOwnerUserId); out.endTag(null, TAG_DEVICE_OWNER_CONTEXT); + + } + + if (!mDeviceOwnerTypes.isEmpty()) { + for (ArrayMap.Entry<String, Integer> entry : mDeviceOwnerTypes.entrySet()) { + out.startTag(null, TAG_DEVICE_OWNER_TYPE); + out.attribute(null, ATTR_PACKAGE, entry.getKey()); + out.attributeInt(null, ATTR_DEVICE_OWNER_TYPE_VALUE, entry.getValue()); + out.endTag(null, TAG_DEVICE_OWNER_TYPE); + } } if (mSystemUpdatePolicy != null) { @@ -942,6 +996,12 @@ class Owners { } } break; + case TAG_DEVICE_OWNER_TYPE: + String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); + int deviceOwnerType = parser.getAttributeInt(null, ATTR_DEVICE_OWNER_TYPE_VALUE, + DEVICE_OWNER_TYPE_DEFAULT); + mDeviceOwnerTypes.put(packageName, deviceOwnerType); + break; default: Slog.e(TAG, "Unexpected tag: " + tag); return false; diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index dd2dd8150165..6bbd320017a7 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -104,7 +104,6 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.internal.widget.ILockSettings; import com.android.server.am.ActivityManagerService; import com.android.server.appbinding.AppBindingService; -import com.android.server.apphibernation.AppHibernationService; import com.android.server.attention.AttentionManagerService; import com.android.server.audio.AudioService; import com.android.server.biometrics.AuthService; @@ -1718,14 +1717,9 @@ public final class SystemServer implements Dumpable { startTextToSpeechManagerService(context, t); // System Speech Recognition Service - if (deviceHasConfigString(context, - R.string.config_defaultOnDeviceSpeechRecognitionService)) { - t.traceBegin("StartSpeechRecognitionManagerService"); - mSystemServiceManager.startService(SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS); - t.traceEnd(); - } else { - Slog.d(TAG, "System speech recognition is not defined by OEM"); - } + t.traceBegin("StartSpeechRecognitionManagerService"); + mSystemServiceManager.startService(SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS); + t.traceEnd(); // App prediction manager service if (deviceHasConfigString(context, R.string.config_defaultAppPredictionService)) { @@ -1785,7 +1779,7 @@ public final class SystemServer implements Dumpable { t.traceBegin("StartIpSecService"); try { - ipSecService = IpSecService.create(context, networkManagement); + ipSecService = IpSecService.create(context); ServiceManager.addService(Context.IPSEC_SERVICE, ipSecService); } catch (Throwable e) { reportWtf("starting IpSec Service", e); @@ -2150,11 +2144,9 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS); t.traceEnd(); - if (AppHibernationService.isAppHibernationEnabled()) { - t.traceBegin("StartAppHibernationService"); - mSystemServiceManager.startService(APP_HIBERNATION_SERVICE_CLASS); - t.traceEnd(); - } + t.traceBegin("StartAppHibernationService"); + mSystemServiceManager.startService(APP_HIBERNATION_SERVICE_CLASS); + t.traceEnd(); if (GestureLauncherService.isGestureLauncherEnabled(context.getResources())) { t.traceBegin("StartGestureLauncher"); diff --git a/services/musicrecognition/Android.bp b/services/musicrecognition/Android.bp index fea9efa9dde5..8298dec29884 100644 --- a/services/musicrecognition/Android.bp +++ b/services/musicrecognition/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + filegroup { name: "services.musicsearch-sources", srcs: ["java/**/*.java"], @@ -10,4 +19,4 @@ java_library_static { defaults: ["platform_service_defaults"], srcs: [":services.musicsearch-sources"], libs: ["services.core", "app-compat-annotations"], -}
\ No newline at end of file +} diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java index cbebe6984794..2219d477630e 100644 --- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java +++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java @@ -38,7 +38,6 @@ import static org.testng.Assert.expectThrows; import android.annotation.UserIdInt; import android.app.Application; -import android.app.backup.BackupManager.OperationType; import android.app.backup.IBackupManagerMonitor; import android.app.backup.IBackupObserver; import android.app.backup.IFullBackupRestoreObserver; @@ -874,8 +873,7 @@ public class BackupManagerServiceRoboTest { SecurityException.class, () -> backupManagerService.requestBackup( - mUserTwoId, packages, observer, monitor, 0, - OperationType.BACKUP)); + mUserTwoId, packages, observer, monitor, 0)); } /** @@ -893,11 +891,9 @@ public class BackupManagerServiceRoboTest { IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true); - backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0, - OperationType.BACKUP); + backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0); - verify(mUserTwoService).requestBackup(packages, observer, monitor, /* flags */ 0, - OperationType.BACKUP); + verify(mUserTwoService).requestBackup(packages, observer, monitor, /* flags */ 0); } /** Test that the backup service routes methods correctly to the user that requests it. */ @@ -910,11 +906,9 @@ public class BackupManagerServiceRoboTest { IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.requestBackup(mUserOneId, packages, observer, monitor, /* flags */ 0, - OperationType.BACKUP); + backupManagerService.requestBackup(mUserOneId, packages, observer, monitor, /* flags */ 0); - verify(mUserOneService).requestBackup(packages, observer, monitor, /* flags */ 0, - OperationType.BACKUP); + verify(mUserOneService).requestBackup(packages, observer, monitor, /* flags */ 0); } /** Test that the backup service routes methods correctly to the user that requests it. */ @@ -927,11 +921,9 @@ public class BackupManagerServiceRoboTest { IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0, - OperationType.BACKUP); + backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0); - verify(mUserOneService, never()).requestBackup(packages, observer, monitor, /* flags */ 0, - OperationType.BACKUP); + verify(mUserOneService, never()).requestBackup(packages, observer, monitor, /* flags */ 0); } /** @@ -1092,11 +1084,9 @@ public class BackupManagerServiceRoboTest { registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT, - OperationType.BACKUP); + backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT); - verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT, - OperationType.BACKUP); + verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT); } /** Test that the backup service does not route methods for non-registered users. */ @@ -1106,11 +1096,9 @@ public class BackupManagerServiceRoboTest { registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT, - OperationType.BACKUP); + backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT); - verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT, - OperationType.BACKUP); + verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT); } /** Test that the backup service routes methods correctly to the user that requests it. */ diff --git a/services/searchui/Android.bp b/services/searchui/Android.bp index cc632940e4a6..3081a5111ab7 100644 --- a/services/searchui/Android.bp +++ b/services/searchui/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + filegroup { name: "services.searchui-sources", srcs: ["java/**/*.java"], diff --git a/services/smartspace/Android.bp b/services/smartspace/Android.bp index fcf780d4d927..640a88dbaa24 100644 --- a/services/smartspace/Android.bp +++ b/services/smartspace/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + filegroup { name: "services.smartspace-sources", srcs: ["java/**/*.java"], diff --git a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp index b7a0624e02b8..e70a734cf271 100644 --- a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + java_library { name: "PackageManagerServiceHostTestsIntentVerifyUtils", srcs: ["src/**/*.kt"], diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp index af0ac77eaadd..7e4f0e72b62d 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test_helper_app { name: "PackageManagerServiceDeviceSideTests", sdk_version: "test_current", diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp index e82f57d20fcc..4f3f2eb1b58e 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test_helper_app { name: "PackageManagerTestIntentVerifier", srcs: [ "src/**/*.kt" ], diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp index 7161fdd33516..9f9ed24c63bc 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test_helper_app { name: "PackageManagerTestIntentVerifierTarget1", manifest: "AndroidManifest1.xml", diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp index 58f17f253a24..ebad5afac625 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test_helper_app { name: "PackageManagerTestAppDeclaresStaticLibrary", manifest: "AndroidManifestDeclaresStaticLibrary.xml", diff --git a/services/tests/PackageManagerServiceTests/unit/Android.bp b/services/tests/PackageManagerServiceTests/unit/Android.bp index 4aa8abc84392..334e53a3aec7 100644 --- a/services/tests/PackageManagerServiceTests/unit/Android.bp +++ b/services/tests/PackageManagerServiceTests/unit/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test { name: "PackageManagerServiceUnitTests", srcs: ["src/**/*.kt"], diff --git a/services/tests/inprocesstests/Android.bp b/services/tests/inprocesstests/Android.bp index 6dd059fc919d..7c237ac6befb 100644 --- a/services/tests/inprocesstests/Android.bp +++ b/services/tests/inprocesstests/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test { name: "FrameworksInProcessTests", srcs: ["src/**/*.java"], diff --git a/services/tests/mockingservicestests/jni/Android.bp b/services/tests/mockingservicestests/jni/Android.bp index 928065a7ebd9..a32bf2c3b260 100644 --- a/services/tests/mockingservicestests/jni/Android.bp +++ b/services/tests/mockingservicestests/jni/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + cc_library_shared { name: "libactivitymanagermockingservicestestjni", diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java index 56d30ccdf59f..20a58426f1eb 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java @@ -139,7 +139,8 @@ public final class CachedAppOptimizerTest { app.info.uid = packageUid; // Exact value does not mater, it can be any state for which compaction is allowed. app.mState.setSetProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE); - app.mState.setSetAdj(905); + app.mState.setSetAdj(899); + app.mState.setCurAdj(940); return app; } @@ -164,8 +165,6 @@ public final class CachedAppOptimizerTest { CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE); assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo( CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE); - assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo( - CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB); assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo( CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5); assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo( @@ -176,6 +175,11 @@ public final class CachedAppOptimizerTest { CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB); assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo( CachedAppOptimizer.DEFAULT_USE_FREEZER); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMinOomAdj).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMaxOomAdj).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ); + Set<Integer> expected = new HashSet<>(); for (String s : TextUtils.split( @@ -231,6 +235,14 @@ public final class CachedAppOptimizerTest { Long.toString( CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1), false); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_MIN_OOM_ADJ, + Long.toString( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ + 10), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_MAX_OOM_ADJ, + Long.toString( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ - 10), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,2,3", false); assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo( CachedAppOptimizer.DEFAULT_USE_FREEZER); @@ -261,6 +273,12 @@ public final class CachedAppOptimizerTest { CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1); assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo( CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1); + assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMinOomAdj).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ + 10); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMaxOomAdj).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ - 10); assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo( CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f); assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo( @@ -437,7 +455,7 @@ public final class CachedAppOptimizerTest { mCachedAppOptimizerUnderTest.init(); // When we override new reasonable throttle values after init... - mCountDown = new CountDownLatch(6); + mCountDown = new CountDownLatch(8); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, CachedAppOptimizer.KEY_COMPACT_THROTTLE_1, Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1), false); @@ -456,7 +474,13 @@ public final class CachedAppOptimizerTest { DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, CachedAppOptimizer.KEY_COMPACT_THROTTLE_6, Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1), false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_MIN_OOM_ADJ, + Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_MAX_OOM_ADJ, + Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ - 1), false); + assertThat(mCountDown.await(7, TimeUnit.SECONDS)).isTrue(); // Then those flags values are reflected in the compactor. assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo( @@ -471,6 +495,10 @@ public final class CachedAppOptimizerTest { CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1); assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo( CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMinOomAdj).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ + 1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMaxOomAdj).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ - 1); } @Test @@ -902,7 +930,6 @@ public final class CachedAppOptimizerTest { valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats.get( pid).getRssAfterCompaction(); assertThat(valuesAfter).isEqualTo(rssAfter3); - } @Test @@ -954,6 +981,54 @@ public final class CachedAppOptimizerTest { assertThat(valuesAfter).isEqualTo(rssAboveThresholdAfter); } + @Test + public void processWithOomAdjTooSmall_notFullCompacted() throws Exception { + // Initialize CachedAppOptimizer and set flags to (1) enable compaction, (2) set Min and + // Max OOM_Adj throttles. + mCachedAppOptimizerUnderTest.init(); + setFlag(CachedAppOptimizer.KEY_USE_COMPACTION, "true", true); + setFlag(CachedAppOptimizer.KEY_COMPACT_THROTTLE_MIN_OOM_ADJ, Long.toString(920), true); + setFlag(CachedAppOptimizer.KEY_COMPACT_THROTTLE_MAX_OOM_ADJ, Long.toString(950), true); + initActivityManagerService(); + + // Simulate RSS memory for which compaction should occur. + long[] rssBefore = + new long[]{/*Total RSS*/ 15000, /*File RSS*/ 15000, /*Anon RSS*/ 15000, + /*Swap*/ 10000}; + long[] rssAfter = + new long[]{/*Total RSS*/ 8000, /*File RSS*/ 9000, /*Anon RSS*/ 6000, /*Swap*/5000}; + // Process that passes properties. + int pid = 1; + ProcessRecord processRecord = + makeProcessRecord(pid, 2, 3, "p1", "app1"); + mProcessDependencies.setRss(rssBefore); + mProcessDependencies.setRssAfterCompaction(rssAfter); + + // Compaction should occur if (setAdj < min for process || setAdj > max for process) && + // (MIN < curAdj < MAX) + // GIVEN OomAdj score below threshold. + processRecord.mState.setSetAdj(899); + processRecord.mState.setCurAdj(970); + // WHEN we try to run compaction + mCachedAppOptimizerUnderTest.compactAppFull(processRecord); + waitForHandler(); + // THEN process IS NOT compacted. + assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull(); + + // GIVEN (setAdj < MIN || setAdj > MAX) && (MIN < curAdj < MAX) + processRecord.mState.setSetAdj(910); + processRecord.mState.setCurAdj(930); + // WHEN we try to run compaction + mCachedAppOptimizerUnderTest.compactAppFull(processRecord); + waitForHandler(); + // THEN process IS compacted. + assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull(); + long[] valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats + .get(pid) + .getRssAfterCompaction(); + assertThat(valuesAfter).isEqualTo(rssAfter); + } + private void setFlag(String key, String value, boolean defaultValue) throws Exception { mCountDown = new CountDownLatch(1); diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java index a382e85538cd..1c45203bb3c9 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -46,6 +46,7 @@ import static com.android.server.am.ProcessList.HEAVY_WEIGHT_APP_ADJ; import static com.android.server.am.ProcessList.HOME_APP_ADJ; import static com.android.server.am.ProcessList.PERCEPTIBLE_APP_ADJ; import static com.android.server.am.ProcessList.PERCEPTIBLE_LOW_APP_ADJ; +import static com.android.server.am.ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ; import static com.android.server.am.ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ; import static com.android.server.am.ProcessList.PERSISTENT_PROC_ADJ; import static com.android.server.am.ProcessList.PERSISTENT_SERVICE_ADJ; @@ -877,6 +878,39 @@ public class MockingOomAdjusterTests { @SuppressWarnings("GuardedBy") @Test + public void testUpdateOomAdj_DoOne_Service_MediumPerceptible() { + { + ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, + MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); + ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, + MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); + bindService(app, client, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); + client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); + + assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ, app.mState.getSetAdj()); + } + + { + ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, + MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); + ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, + MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); + WindowProcessController wpc = client.getWindowProcessController(); + doReturn(true).when(wpc).isHeavyWeightProcess(); + bindService(app, client, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); + client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); + doReturn(false).when(wpc).isHeavyWeightProcess(); + + assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ, app.mState.getSetAdj()); + } + } + + @SuppressWarnings("GuardedBy") + @Test public void testUpdateOomAdj_DoOne_Service_Other() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java index 7f8784dc2599..728b97cc3968 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java @@ -25,8 +25,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; @@ -94,11 +96,13 @@ public class LocalDisplayAdapterTest { private Injector mInjector; + @Mock + private LocalDisplayAdapter.SurfaceControlProxy mSurfaceControlProxy; + @Before public void setUp() throws Exception { mMockitoSession = mockitoSession() .initMocks(this) - .mockStatic(SurfaceControl.class) .strictness(Strictness.LENIENT) .startMocking(); mHandler = new Handler(Looper.getMainLooper()); @@ -227,6 +231,7 @@ public class LocalDisplayAdapterTest { setUpDisplay(display); mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + assertTrue(mListener.traversalRequested); assertThat(mListener.changedDisplays.size()).isGreaterThan(0); // Verify the supported modes are updated accordingly. @@ -333,6 +338,7 @@ public class LocalDisplayAdapterTest { mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + assertTrue(mListener.traversalRequested); assertThat(mListener.addedDisplays.size()).isEqualTo(1); assertThat(mListener.changedDisplays.size()).isEqualTo(1); @@ -380,6 +386,7 @@ public class LocalDisplayAdapterTest { mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + assertTrue(mListener.traversalRequested); assertThat(mListener.addedDisplays.size()).isEqualTo(1); assertThat(mListener.changedDisplays.size()).isEqualTo(1); @@ -418,6 +425,7 @@ public class LocalDisplayAdapterTest { mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + assertTrue(mListener.traversalRequested); assertThat(mListener.addedDisplays.size()).isEqualTo(1); assertThat(mListener.changedDisplays.size()).isEqualTo(1); @@ -429,6 +437,74 @@ public class LocalDisplayAdapterTest { } @Test + public void testAfterDisplayChange_AllmSupportIsUpdated() throws Exception { + FakeDisplay display = new FakeDisplay(PORT_A); + display.dynamicInfo.autoLowLatencyModeSupported = true; + setUpDisplay(display); + updateAvailableDisplays(); + mAdapter.registerLocked(); + waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + + assertThat(mListener.addedDisplays.size()).isEqualTo(1); + assertThat(mListener.changedDisplays).isEmpty(); + + DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(0) + .getDisplayDeviceInfoLocked(); + + assertThat(displayDeviceInfo.allmSupported).isTrue(); + + // Change the display + display.dynamicInfo.autoLowLatencyModeSupported = false; + setUpDisplay(display); + mInjector.getTransmitter().sendHotplug(display, /* connected */ true); + waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + + assertTrue(mListener.traversalRequested); + assertThat(mListener.addedDisplays.size()).isEqualTo(1); + assertThat(mListener.changedDisplays.size()).isEqualTo(1); + + DisplayDevice displayDevice = mListener.changedDisplays.get(0); + displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); + displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked(); + + assertThat(displayDeviceInfo.allmSupported).isFalse(); + } + + @Test + public void testAfterDisplayChange_GameContentTypeSupportIsUpdated() throws Exception { + FakeDisplay display = new FakeDisplay(PORT_A); + display.dynamicInfo.gameContentTypeSupported = true; + setUpDisplay(display); + updateAvailableDisplays(); + mAdapter.registerLocked(); + waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + + assertThat(mListener.addedDisplays.size()).isEqualTo(1); + assertThat(mListener.changedDisplays).isEmpty(); + + DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(0) + .getDisplayDeviceInfoLocked(); + + assertThat(displayDeviceInfo.gameContentTypeSupported).isTrue(); + + // Change the display + display.dynamicInfo.gameContentTypeSupported = false; + setUpDisplay(display); + mInjector.getTransmitter().sendHotplug(display, /* connected */ true); + waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + + assertTrue(mListener.traversalRequested); + assertThat(mListener.addedDisplays.size()).isEqualTo(1); + assertThat(mListener.changedDisplays.size()).isEqualTo(1); + + DisplayDevice displayDevice = mListener.changedDisplays.get(0); + displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); + displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked(); + + assertThat(displayDeviceInfo.gameContentTypeSupported).isFalse(); + } + + @Test public void testAfterDisplayChange_ColorModesAreUpdated() throws Exception { FakeDisplay display = new FakeDisplay(PORT_A); final int[] initialColorModes = new int[]{Display.COLOR_MODE_BT709}; @@ -454,6 +530,7 @@ public class LocalDisplayAdapterTest { mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + assertTrue(mListener.traversalRequested); assertThat(mListener.addedDisplays.size()).isEqualTo(1); assertThat(mListener.changedDisplays.size()).isEqualTo(1); @@ -480,6 +557,30 @@ public class LocalDisplayAdapterTest { mAdapter.registerLocked(); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + assertThat(mListener.addedDisplays.size()).isEqualTo(1); + DisplayDevice displayDevice = mListener.addedDisplays.get(0); + + int baseModeId = Arrays.stream(displayDevice.getDisplayDeviceInfoLocked().supportedModes) + .filter(mode -> mode.getRefreshRate() == 60f) + .findFirst() + .get() + .getModeId(); + + displayDevice.setDesiredDisplayModeSpecsLocked( + new DisplayModeDirector.DesiredDisplayModeSpecs( + /*baseModeId*/ baseModeId, + /*allowGroupSwitching*/ false, + new DisplayModeDirector.RefreshRateRange(60f, 60f), + new DisplayModeDirector.RefreshRateRange(60f, 60f) + )); + verify(mSurfaceControlProxy).setDesiredDisplayModeSpecs(display.token, + new SurfaceControl.DesiredDisplayModeSpecs( + /* baseModeId */ 0, + /* allowGroupSwitching */ false, + /* primaryRange */ 60f, 60f, + /* appRange */ 60f, 60f + )); + // Change the display display.dynamicInfo.supportedDisplayModes = new SurfaceControl.DisplayMode[]{ createFakeDisplayMode(2, 1920, 1080, 60f) @@ -489,36 +590,64 @@ public class LocalDisplayAdapterTest { display.desiredDisplayModeSpecs.defaultMode = 1; setUpDisplay(display); - updateAvailableDisplays(); mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + + assertTrue(mListener.traversalRequested); + + displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); + + baseModeId = displayDevice.getDisplayDeviceInfoLocked().supportedModes[0].getModeId(); + + // The traversal request will call setDesiredDisplayModeSpecsLocked on the display device + displayDevice.setDesiredDisplayModeSpecsLocked( + new DisplayModeDirector.DesiredDisplayModeSpecs( + /*baseModeId*/ baseModeId, + /*allowGroupSwitching*/ false, + new DisplayModeDirector.RefreshRateRange(60f, 60f), + new DisplayModeDirector.RefreshRateRange(60f, 60f) + )); + + waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + + // Verify that this will reapply the desired modes. + verify(mSurfaceControlProxy).setDesiredDisplayModeSpecs(display.token, + new SurfaceControl.DesiredDisplayModeSpecs( + /* baseModeId */ 2, + /* allowGroupSwitching */ false, + /* primaryRange */ 60f, 60f, + /* appRange */ 60f, 60f + )); } @Test public void testBacklightAdapter_withSurfaceControlSupport() { final Binder displayToken = new Binder(); - doReturn(true).when(() -> SurfaceControl.getDisplayBrightnessSupport(displayToken)); + + when(mSurfaceControlProxy.getDisplayBrightnessSupport(displayToken)).thenReturn(true); // Test as default display - BacklightAdapter ba = new BacklightAdapter(displayToken, true /*isDefault*/); + BacklightAdapter ba = new BacklightAdapter(displayToken, true /*isDefault*/, + mSurfaceControlProxy); ba.setBrightness(0.514f); - verify(() -> SurfaceControl.setDisplayBrightness(displayToken, 0.514f)); + verify(mSurfaceControlProxy).setDisplayBrightness(displayToken, 0.514f); // Test as not default display - BacklightAdapter ba2 = new BacklightAdapter(displayToken, - false /*isDefault*/); + BacklightAdapter ba2 = new BacklightAdapter(displayToken, false /*isDefault*/, + mSurfaceControlProxy); ba2.setBrightness(0.323f); - verify(() -> SurfaceControl.setDisplayBrightness(displayToken, 0.323f)); + verify(mSurfaceControlProxy).setDisplayBrightness(displayToken, 0.323f); } @Test public void testBacklightAdapter_withoutSourceControlSupport_defaultDisplay() { final Binder displayToken = new Binder(); - doReturn(false).when(() -> SurfaceControl.getDisplayBrightnessSupport(displayToken)); + when(mSurfaceControlProxy.getDisplayBrightnessSupport(displayToken)).thenReturn(false); doReturn(mMockedBacklight).when(mMockedLightsManager) .getLight(LightsManager.LIGHT_ID_BACKLIGHT); - BacklightAdapter ba = new BacklightAdapter(displayToken, true /*isDefault*/); + BacklightAdapter ba = new BacklightAdapter(displayToken, true /*isDefault*/, + mSurfaceControlProxy); ba.setBrightness(0.123f); verify(mMockedBacklight).setBrightness(0.123f); } @@ -526,11 +655,12 @@ public class LocalDisplayAdapterTest { @Test public void testBacklightAdapter_withoutSourceControlSupport_nonDefaultDisplay() { final Binder displayToken = new Binder(); - doReturn(false).when(() -> SurfaceControl.getDisplayBrightnessSupport(displayToken)); + when(mSurfaceControlProxy.getDisplayBrightnessSupport(displayToken)).thenReturn(false); doReturn(mMockedBacklight).when(mMockedLightsManager) .getLight(LightsManager.LIGHT_ID_BACKLIGHT); - BacklightAdapter ba = new BacklightAdapter(displayToken, false /*isDefault*/); + BacklightAdapter ba = new BacklightAdapter(displayToken, false /*isDefault*/, + mSurfaceControlProxy); ba.setBrightness(0.456f); // Adapter does not forward any brightness in this case. @@ -618,13 +748,14 @@ public class LocalDisplayAdapterTest { private void setUpDisplay(FakeDisplay display) { mAddresses.add(display.address); - doReturn(display.token).when(() -> - SurfaceControl.getPhysicalDisplayToken(display.address.getPhysicalDisplayId())); - doReturn(display.info).when(() -> SurfaceControl.getStaticDisplayInfo(display.token)); - doReturn(display.dynamicInfo).when( - () -> SurfaceControl.getDynamicDisplayInfo(display.token)); - doReturn(display.desiredDisplayModeSpecs) - .when(() -> SurfaceControl.getDesiredDisplayModeSpecs(display.token)); + when(mSurfaceControlProxy.getPhysicalDisplayToken(display.address.getPhysicalDisplayId())) + .thenReturn(display.token); + when(mSurfaceControlProxy.getStaticDisplayInfo(display.token)) + .thenReturn(display.info); + when(mSurfaceControlProxy.getDynamicDisplayInfo(display.token)) + .thenReturn(display.dynamicInfo); + when(mSurfaceControlProxy.getDesiredDisplayModeSpecs(display.token)) + .thenReturn(display.desiredDisplayModeSpecs); } private void updateAvailableDisplays() { @@ -634,7 +765,7 @@ public class LocalDisplayAdapterTest { ids[i] = address.getPhysicalDisplayId(); i++; } - doReturn(ids).when(() -> SurfaceControl.getPhysicalDisplayIds()); + when(mSurfaceControlProxy.getPhysicalDisplayIds()).thenReturn(ids); } private static DisplayAddress.Physical createDisplayAddress(int port) { @@ -649,7 +780,7 @@ public class LocalDisplayAdapterTest { private static SurfaceControl.DisplayMode createFakeDisplayMode(int id, int width, int height, float refreshRate) { - return createFakeDisplayMode(id, width, height, refreshRate, 0); + return createFakeDisplayMode(id, width, height, refreshRate, /* group */ 0); } private static SurfaceControl.DisplayMode createFakeDisplayMode(int id, int width, int height, @@ -702,14 +833,21 @@ public class LocalDisplayAdapterTest { LocalDisplayAdapter.DisplayEventListener listener) { mTransmitter = new HotplugTransmitter(looper, listener); } + public HotplugTransmitter getTransmitter() { return mTransmitter; } + + @Override + public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() { + return mSurfaceControlProxy; + } } private class TestListener implements DisplayAdapter.Listener { public ArrayList<DisplayDevice> addedDisplays = new ArrayList<>(); public ArrayList<DisplayDevice> changedDisplays = new ArrayList<>(); + public boolean traversalRequested = false; @Override public void onDisplayDeviceEvent(DisplayDevice device, int event) { @@ -722,6 +860,8 @@ public class LocalDisplayAdapterTest { @Override public void onTraversalRequested() { + traversalRequested = true; } } + } diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index 88a691bbc209..bf95f4c51d96 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -31,6 +31,7 @@ import static com.android.server.job.JobSchedulerService.RARE_INDEX; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import static com.android.server.job.JobSchedulerService.WORKING_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; +import static com.android.server.job.JobSchedulerService.sSystemClock; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -58,6 +59,7 @@ import android.app.AppGlobals; import android.app.IActivityManager; import android.app.IUidObserver; import android.app.job.JobInfo; +import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; @@ -85,6 +87,7 @@ import com.android.server.job.JobSchedulerService; import com.android.server.job.JobStore; import com.android.server.job.controllers.QuotaController.ExecutionStats; import com.android.server.job.controllers.QuotaController.QcConstants; +import com.android.server.job.controllers.QuotaController.ShrinkableDebits; import com.android.server.job.controllers.QuotaController.TimingSession; import com.android.server.usage.AppStandbyInternal; @@ -125,6 +128,7 @@ public class QuotaControllerTest { private int mSourceUid; private PowerAllowlistInternal.TempAllowlistChangeListener mTempAllowlistListener; private IUidObserver mUidObserver; + private UsageStatsManagerInternal.UsageEventListener mUsageEventListener; DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder; private MockitoSession mMockingSession; @@ -218,6 +222,8 @@ public class QuotaControllerTest { ArgumentCaptor.forClass(IUidObserver.class); ArgumentCaptor<PowerAllowlistInternal.TempAllowlistChangeListener> taChangeCaptor = ArgumentCaptor.forClass(PowerAllowlistInternal.TempAllowlistChangeListener.class); + ArgumentCaptor<UsageStatsManagerInternal.UsageEventListener> ueListenerCaptor = + ArgumentCaptor.forClass(UsageStatsManagerInternal.UsageEventListener.class); mQuotaController = new QuotaController(mJobSchedulerService, mock(BackgroundJobsController.class), mock(ConnectivityController.class)); @@ -229,6 +235,8 @@ public class QuotaControllerTest { verify(mPowerAllowlistInternal) .registerTempAllowlistChangeListener(taChangeCaptor.capture()); mTempAllowlistListener = taChangeCaptor.getValue(); + verify(mUsageStatsManager).registerListener(ueListenerCaptor.capture()); + mUsageEventListener = ueListenerCaptor.getValue(); try { verify(activityManager).registerUidObserver( uidObserverCaptor.capture(), @@ -288,7 +296,7 @@ public class QuotaControllerTest { verify(foregroundUids, timeout(2 * SECOND_IN_MILLIS).times(1)).delete(eq(uid)); assertFalse(foregroundUids.get(uid)); } - waitForQuietBackground(); + waitForNonDelayedMessagesProcessed(); } catch (Exception e) { fail("exception encountered: " + e.getMessage()); } @@ -385,13 +393,8 @@ public class QuotaControllerTest { } } - private void waitForQuietBackground() throws Exception { - for (int i = 0; i < 5; ++i) { - if (!mQuotaController.isActiveBackgroundProcessing()) { - break; - } - Thread.sleep(500); - } + private void waitForNonDelayedMessagesProcessed() { + mQuotaController.getHandler().runWithScissors(() -> {}, 15_000); } @Test @@ -1330,7 +1333,9 @@ public class QuotaControllerTest { timeUsedMs, 5), true); JobStatus job = createExpeditedJobStatus("testGetMaxJobExecutionTimeLocked_EJ", 0); setStandbyBucket(RARE_INDEX, job); - mQuotaController.maybeStartTrackingJobLocked(job, null); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job, null); + } setCharging(); synchronized (mQuotaController.mLock) { @@ -5158,4 +5163,257 @@ public class QuotaControllerTest { eq(10 * SECOND_IN_MILLIS)); verify(handler, never()).sendMessageDelayed(any(), eq(remainingTimeMs)); } + + @Test + public void testEJDebitTallying() { + setStandbyBucket(RARE_INDEX); + setProcessState(ActivityManager.PROCESS_STATE_SERVICE); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS); + // 15 seconds for each 30 second chunk. + setDeviceConfigLong(QcConstants.KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, 30 * SECOND_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 15 * SECOND_IN_MILLIS); + + // No history. Debits should be 0. + ShrinkableDebits debit = mQuotaController.getEJDebitsLocked(SOURCE_USER_ID, SOURCE_PACKAGE); + assertEquals(0, debit.getTallyLocked()); + assertEquals(10 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // Regular job shouldn't affect EJ tally. + JobStatus regJob = createJobStatus("testEJDebitTallying", 1); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(regJob, null); + mQuotaController.prepareForExecutionLocked(regJob); + } + advanceElapsedClock(5000); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(regJob, null, false); + } + assertEquals(0, debit.getTallyLocked()); + assertEquals(10 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // EJ job should affect EJ tally. + JobStatus eJob = createExpeditedJobStatus("testEJDebitTallying", 2); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(eJob, null); + mQuotaController.prepareForExecutionLocked(eJob); + } + advanceElapsedClock(5 * MINUTE_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(eJob, null, false); + } + assertEquals(5 * MINUTE_IN_MILLIS, debit.getTallyLocked()); + assertEquals(5 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // Instantaneous event for a different user shouldn't affect tally. + advanceElapsedClock(5 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, MINUTE_IN_MILLIS); + + UsageEvents.Event event = + new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis()); + event.mPackage = SOURCE_PACKAGE; + mUsageEventListener.onUsageEvent(SOURCE_USER_ID + 10, event); + assertEquals(5 * MINUTE_IN_MILLIS, debit.getTallyLocked()); + + // Instantaneous event for correct user should reduce tally. + advanceElapsedClock(5 * MINUTE_IN_MILLIS); + + mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); + waitForNonDelayedMessagesProcessed(); + assertEquals(4 * MINUTE_IN_MILLIS, debit.getTallyLocked()); + assertEquals(6 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // Activity start shouldn't reduce tally, but duration with activity started should affect + // remaining EJ time. + advanceElapsedClock(5 * MINUTE_IN_MILLIS); + event = new UsageEvents.Event(UsageEvents.Event.ACTIVITY_RESUMED, sSystemClock.millis()); + event.mPackage = SOURCE_PACKAGE; + mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); + waitForNonDelayedMessagesProcessed(); + advanceElapsedClock(30 * SECOND_IN_MILLIS); + assertEquals(4 * MINUTE_IN_MILLIS, debit.getTallyLocked()); + assertEquals(6 * MINUTE_IN_MILLIS + 15 * SECOND_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + advanceElapsedClock(30 * SECOND_IN_MILLIS); + assertEquals(4 * MINUTE_IN_MILLIS, debit.getTallyLocked()); + assertEquals(6 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // With activity pausing/stopping/destroying, tally should be updated. + advanceElapsedClock(MINUTE_IN_MILLIS); + event = new UsageEvents.Event(UsageEvents.Event.ACTIVITY_DESTROYED, sSystemClock.millis()); + event.mPackage = SOURCE_PACKAGE; + mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); + waitForNonDelayedMessagesProcessed(); + assertEquals(3 * MINUTE_IN_MILLIS, debit.getTallyLocked()); + assertEquals(7 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + @Test + public void testEJDebitTallying_StaleSession() { + setStandbyBucket(RARE_INDEX); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS); + + final long nowElapsed = sElapsedRealtimeClock.millis(); + TimingSession ts = new TimingSession(nowElapsed, nowElapsed + 10 * MINUTE_IN_MILLIS, 5); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, ts, true); + + // Make the session stale. + advanceElapsedClock(12 * MINUTE_IN_MILLIS + mQcConstants.EJ_WINDOW_SIZE_MS); + + // With lazy deletion, we don't update the tally until getRemainingEJExecutionTimeLocked() + // is called, so call that first. + assertEquals(10 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + ShrinkableDebits debit = mQuotaController.getEJDebitsLocked(SOURCE_USER_ID, SOURCE_PACKAGE); + assertEquals(0, debit.getTallyLocked()); + } + + /** + * Tests that rewards are properly accounted when there's no EJ running and the rewards exceed + * the accumulated debits. + */ + @Test + public void testEJDebitTallying_RewardExceedDebits_NoActiveSession() { + setStandbyBucket(WORKING_INDEX); + setProcessState(ActivityManager.PROCESS_STATE_SERVICE); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 30 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, MINUTE_IN_MILLIS); + + final long nowElapsed = sElapsedRealtimeClock.millis(); + TimingSession ts = new TimingSession(nowElapsed - 5 * MINUTE_IN_MILLIS, + nowElapsed - 4 * MINUTE_IN_MILLIS, 2); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, ts, true); + + ShrinkableDebits debit = mQuotaController.getEJDebitsLocked(SOURCE_USER_ID, SOURCE_PACKAGE); + assertEquals(MINUTE_IN_MILLIS, debit.getTallyLocked()); + assertEquals(29 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(30 * SECOND_IN_MILLIS); + UsageEvents.Event event = + new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis()); + event.mPackage = SOURCE_PACKAGE; + mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); + waitForNonDelayedMessagesProcessed(); + assertEquals(0, debit.getTallyLocked()); + assertEquals(30 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(MINUTE_IN_MILLIS); + assertEquals(0, debit.getTallyLocked()); + assertEquals(30 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // Excessive rewards don't increase maximum quota. + event = new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis()); + event.mPackage = SOURCE_PACKAGE; + mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); + waitForNonDelayedMessagesProcessed(); + assertEquals(0, debit.getTallyLocked()); + assertEquals(30 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + /** + * Tests that rewards are properly accounted when there's an active EJ running and the rewards + * exceed the accumulated debits. + */ + @Test + public void testEJDebitTallying_RewardExceedDebits_ActiveSession() { + setStandbyBucket(WORKING_INDEX); + setProcessState(ActivityManager.PROCESS_STATE_SERVICE); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 30 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, MINUTE_IN_MILLIS); + // 15 seconds for each 30 second chunk. + setDeviceConfigLong(QcConstants.KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, 30 * SECOND_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 15 * SECOND_IN_MILLIS); + + final long nowElapsed = sElapsedRealtimeClock.millis(); + TimingSession ts = new TimingSession(nowElapsed - 5 * MINUTE_IN_MILLIS, + nowElapsed - 4 * MINUTE_IN_MILLIS, 2); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, ts, true); + + ShrinkableDebits debit = mQuotaController.getEJDebitsLocked(SOURCE_USER_ID, SOURCE_PACKAGE); + assertEquals(MINUTE_IN_MILLIS, debit.getTallyLocked()); + assertEquals(29 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // With rewards coming in while an EJ is running, the remaining execution time should be + // adjusted accordingly (decrease due to EJ running + increase from reward). + JobStatus eJob = + createExpeditedJobStatus("testEJDebitTallying_RewardExceedDebits_ActiveSession", 1); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(eJob, null); + mQuotaController.prepareForExecutionLocked(eJob); + } + advanceElapsedClock(30 * SECOND_IN_MILLIS); + assertEquals(MINUTE_IN_MILLIS, debit.getTallyLocked()); + assertEquals(28 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(30 * SECOND_IN_MILLIS); + UsageEvents.Event event = + new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis()); + event.mPackage = SOURCE_PACKAGE; + mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); + waitForNonDelayedMessagesProcessed(); + assertEquals(0, debit.getTallyLocked()); + assertEquals(29 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(MINUTE_IN_MILLIS); + assertEquals(0, debit.getTallyLocked()); + assertEquals(28 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // Activity start shouldn't reduce tally, but duration with activity started should affect + // remaining EJ time. + event = new UsageEvents.Event(UsageEvents.Event.ACTIVITY_RESUMED, sSystemClock.millis()); + event.mPackage = SOURCE_PACKAGE; + mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); + waitForNonDelayedMessagesProcessed(); + advanceElapsedClock(30 * SECOND_IN_MILLIS); + assertEquals(0, debit.getTallyLocked()); + // Decrease by 30 seconds for running EJ, increase by 15 seconds due to ongoing activity. + assertEquals(27 * MINUTE_IN_MILLIS + 45 * SECOND_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + advanceElapsedClock(30 * SECOND_IN_MILLIS); + assertEquals(0, debit.getTallyLocked()); + assertEquals(27 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(MINUTE_IN_MILLIS); + assertEquals(0, debit.getTallyLocked()); + assertEquals(27 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + event = new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis()); + event.mPackage = SOURCE_PACKAGE; + mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); + waitForNonDelayedMessagesProcessed(); + assertEquals(0, debit.getTallyLocked()); + assertEquals(28 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(MINUTE_IN_MILLIS); + assertEquals(0, debit.getTallyLocked()); + assertEquals(27 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // At this point, with activity pausing/stopping/destroying, since we're giving a reward, + // tally should remain 0, and time remaining shouldn't change since it was accounted for + // at every step. + event = new UsageEvents.Event(UsageEvents.Event.ACTIVITY_DESTROYED, sSystemClock.millis()); + event.mPackage = SOURCE_PACKAGE; + mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); + waitForNonDelayedMessagesProcessed(); + assertEquals(0, debit.getTallyLocked()); + assertEquals(27 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java index 66b037d70a40..68d6557880c4 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java @@ -123,9 +123,10 @@ public class LocationProviderManagerTest { .setPowerUsage(POWER_USAGE_HIGH) .setAccuracy(ProviderProperties.ACCURACY_FINE) .build(); + private static final CallerIdentity PROVIDER_IDENTITY = CallerIdentity.forTest(CURRENT_USER, 1, + "mypackage", "attribution"); private static final CallerIdentity IDENTITY = CallerIdentity.forTest(CURRENT_USER, 1, - "mypackage", - "attribution"); + "mypackage", "attribution", "listener"); private static final WorkSource WORK_SOURCE = new WorkSource(IDENTITY.getUid()); private Random mRandom; @@ -169,7 +170,7 @@ public class LocationProviderManagerTest { mPassive.startManager(); mPassive.setRealProvider(new PassiveLocationProvider(mContext)); - mProvider = new TestProvider(PROPERTIES, IDENTITY); + mProvider = new TestProvider(PROPERTIES, PROVIDER_IDENTITY); mProvider.setProviderAllowed(true); mManager = new LocationProviderManager(mContext, mInjector, eventLog, NAME, mPassive); @@ -351,7 +352,7 @@ public class LocationProviderManagerTest { @Test public void testGetLastLocation_ClearOnMockRemoval() { - MockLocationProvider mockProvider = new MockLocationProvider(PROPERTIES, IDENTITY); + MockLocationProvider mockProvider = new MockLocationProvider(PROPERTIES, PROVIDER_IDENTITY); mockProvider.setAllowed(true); mManager.setMockProvider(mockProvider); @@ -441,7 +442,7 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_SameProcess() throws Exception { CallerIdentity identity = CallerIdentity.forTest(CURRENT_USER, Process.myPid(), "mypackage", - "attribution"); + "attribution", "listener"); ILocationListener listener = createMockLocationListener(); mManager.registerLocationRequest( @@ -477,7 +478,7 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_Unregister_SameProcess() throws Exception { CallerIdentity identity = CallerIdentity.forTest(CURRENT_USER, Process.myPid(), "mypackage", - "attribution"); + "attribution", "listener"); ILocationListener listener = createMockLocationListener(); mManager.registerLocationRequest( @@ -604,7 +605,7 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_Wakelock() throws Exception { CallerIdentity identity = CallerIdentity.forTest(CURRENT_USER, Process.myPid(), "mypackage", - "attribution"); + "attribution", "listener"); ILocationListener listener = createMockLocationListener(); mManager.registerLocationRequest( diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java index 1328b91d03f9..07f67327b2bf 100644 --- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java @@ -110,6 +110,8 @@ public final class AppHibernationServiceTest { UserInfo userInfo = addUser(USER_ID_1); mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo)); doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_1); + + mAppHibernationService.mIsServiceEnabled = true; } @Test 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 73b0105210c4..6890ed1688bb 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 @@ -28,6 +28,7 @@ import android.app.appsearch.SearchSpec; import android.app.appsearch.SetSchemaResponse; import android.app.appsearch.exceptions.AppSearchException; import android.content.Context; +import android.util.ArrayMap; import android.util.ArraySet; import androidx.test.core.app.ApplicationProvider; @@ -55,6 +56,7 @@ import org.junit.rules.TemporaryFolder; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; public class AppSearchImplTest { @@ -971,21 +973,46 @@ public class AppSearchImplTest { } @Test - public void testHasSchemaType() throws Exception { - // Nothing exists yet - assertThat(mAppSearchImpl.hasSchemaTypeLocked("package", "database", "Schema")).isFalse(); + public void testGetPackageToDatabases() throws Exception { + Map<String, Set<String>> existingMapping = mAppSearchImpl.getPackageToDatabases(); + Map<String, Set<String>> expectedMapping = new ArrayMap<>(); + expectedMapping.putAll(existingMapping); + // Has database1 + expectedMapping.put("package1", ImmutableSet.of("database1")); mAppSearchImpl.setSchema( - "package", - "database", - Collections.singletonList(new AppSearchSchema.Builder("Schema").build()), + "package1", + "database1", + Collections.singletonList(new AppSearchSchema.Builder("schema").build()), /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false); - assertThat(mAppSearchImpl.hasSchemaTypeLocked("package", "database", "Schema")).isTrue(); + assertThat(mAppSearchImpl.getPackageToDatabases()) + .containsExactlyEntriesIn(expectedMapping); - assertThat(mAppSearchImpl.hasSchemaTypeLocked("package", "database", "UnknownSchema")) - .isFalse(); + // Has both databases + expectedMapping.put("package1", ImmutableSet.of("database1", "database2")); + mAppSearchImpl.setSchema( + "package1", + "database2", + Collections.singletonList(new AppSearchSchema.Builder("schema").build()), + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*forceOverride=*/ false); + assertThat(mAppSearchImpl.getPackageToDatabases()) + .containsExactlyEntriesIn(expectedMapping); + + // Has both packages + expectedMapping.put("package2", ImmutableSet.of("database1")); + mAppSearchImpl.setSchema( + "package2", + "database1", + Collections.singletonList(new AppSearchSchema.Builder("schema").build()), + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*forceOverride=*/ false); + assertThat(mAppSearchImpl.getPackageToDatabases()) + .containsExactlyEntriesIn(expectedMapping); } @Test diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java new file mode 100644 index 000000000000..4308885faaad --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java @@ -0,0 +1,124 @@ +/* + * 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.stats; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Test; + +public class AppSearchStatsTest { + static final String TEST_PACKAGE_NAME = "com.google.test"; + static final String TEST_DATA_BASE = "testDataBase"; + static final int TEST_STATUS_CODE = 2; + static final int TEST_TOTAL_LATENCY_MILLIS = 20; + + @Test + public void testAppSearchStats_GeneralStats() { + final GeneralStats gStats = + new GeneralStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE) + .setStatusCode(TEST_STATUS_CODE) + .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS) + .build(); + + assertThat(gStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME); + assertThat(gStats.getDatabase()).isEqualTo(TEST_DATA_BASE); + assertThat(gStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE); + assertThat(gStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS); + } + + @Test + public void testAppSearchStats_CallStats() { + final int estimatedBinderLatencyMillis = 1; + final int numOperationsSucceeded = 2; + final int numOperationsFailed = 3; + + final GeneralStats gStats = + new GeneralStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE) + .setStatusCode(TEST_STATUS_CODE) + .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS) + .build(); + final @CallStats.CallType int callType = CallStats.CALL_TYPE_PUT_DOCUMENTS; + final CallStats cStats = + new CallStats.Builder(gStats) + .setCallType(callType) + .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) + .setNumOperationsSucceeded(numOperationsSucceeded) + .setNumOperationsFailed(numOperationsFailed) + .build(); + + assertThat(cStats.getGeneralStats().getPackageName()).isEqualTo(TEST_PACKAGE_NAME); + assertThat(cStats.getGeneralStats().getDatabase()).isEqualTo(TEST_DATA_BASE); + assertThat(cStats.getGeneralStats().getStatusCode()).isEqualTo(TEST_STATUS_CODE); + assertThat(cStats.getGeneralStats().getTotalLatencyMillis()) + .isEqualTo(TEST_TOTAL_LATENCY_MILLIS); + assertThat(cStats.getEstimatedBinderLatencyMillis()) + .isEqualTo(estimatedBinderLatencyMillis); + assertThat(cStats.getCallType()).isEqualTo(callType); + assertThat(cStats.getNumOperationsSucceeded()).isEqualTo(numOperationsSucceeded); + assertThat(cStats.getNumOperationsFailed()).isEqualTo(numOperationsFailed); + } + + @Test + public void testAppSearchStats_PutDocumentStats() { + final int generateDocumentProtoLatencyMillis = 1; + final int rewriteDocumentTypesLatencyMillis = 2; + final int nativeLatencyMillis = 3; + final int nativeDocumentStoreLatencyMillis = 4; + final int nativeIndexLatencyMillis = 5; + final int nativeIndexMergeLatencyMillis = 6; + final int nativeDocumentSize = 7; + final int nativeNumTokensIndexed = 8; + final int nativeNumTokensClipped = 9; + + final GeneralStats gStats = + new GeneralStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE) + .setStatusCode(TEST_STATUS_CODE) + .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS) + .build(); + final PutDocumentStats pStats = + new PutDocumentStats.Builder(gStats) + .setGenerateDocumentProtoLatencyMillis(generateDocumentProtoLatencyMillis) + .setRewriteDocumentTypesLatencyMillis(rewriteDocumentTypesLatencyMillis) + .setNativeLatencyMillis(nativeLatencyMillis) + .setNativeDocumentStoreLatencyMillis(nativeDocumentStoreLatencyMillis) + .setNativeIndexLatencyMillis(nativeIndexLatencyMillis) + .setNativeIndexMergeLatencyMillis(nativeIndexMergeLatencyMillis) + .setNativeDocumentSizeBytes(nativeDocumentSize) + .setNativeNumTokensIndexed(nativeNumTokensIndexed) + .setNativeNumTokensClipped(nativeNumTokensClipped) + .build(); + + assertThat(pStats.getGeneralStats().getPackageName()).isEqualTo(TEST_PACKAGE_NAME); + assertThat(pStats.getGeneralStats().getDatabase()).isEqualTo(TEST_DATA_BASE); + assertThat(pStats.getGeneralStats().getStatusCode()).isEqualTo(TEST_STATUS_CODE); + assertThat(pStats.getGeneralStats().getTotalLatencyMillis()) + .isEqualTo(TEST_TOTAL_LATENCY_MILLIS); + assertThat(pStats.getGenerateDocumentProtoLatencyMillis()) + .isEqualTo(generateDocumentProtoLatencyMillis); + assertThat(pStats.getRewriteDocumentTypesLatencyMillis()) + .isEqualTo(rewriteDocumentTypesLatencyMillis); + assertThat(pStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis); + assertThat(pStats.getNativeDocumentStoreLatencyMillis()) + .isEqualTo(nativeDocumentStoreLatencyMillis); + assertThat(pStats.getNativeIndexLatencyMillis()).isEqualTo(nativeIndexLatencyMillis); + assertThat(pStats.getNativeIndexMergeLatencyMillis()) + .isEqualTo(nativeIndexMergeLatencyMillis); + assertThat(pStats.getNativeDocumentSizeBytes()).isEqualTo(nativeDocumentSize); + assertThat(pStats.getNativeNumTokensIndexed()).isEqualTo(nativeNumTokensIndexed); + assertThat(pStats.getNativeNumTokensClipped()).isEqualTo(nativeNumTokensClipped); + } +} diff --git a/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java index b98f0257d7b7..205ff300c543 100644 --- a/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java @@ -23,6 +23,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; +import android.app.backup.BackupAgent; import android.app.backup.BackupManager.OperationType; import android.app.backup.IBackupManagerMonitor; import android.app.backup.IBackupObserver; @@ -30,6 +31,7 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import com.android.internal.backup.IBackupTransport; import android.platform.test.annotations.Presubmit; import androidx.test.runner.AndroidJUnit4; @@ -56,6 +58,7 @@ public class UserBackupManagerServiceTest { @Mock IBackupObserver mBackupObserver; @Mock PackageManager mPackageManager; @Mock TransportClient mTransportClient; + @Mock IBackupTransport mBackupTransport; @Mock BackupEligibilityRules mBackupEligibilityRules; @@ -132,6 +135,33 @@ public class UserBackupManagerServiceTest { assertThat(params.mBackupEligibilityRules).isEqualTo(mBackupEligibilityRules); } + @Test + public void testGetOperationTypeFromTransport_returnsBackupByDefault() + throws Exception { + when(mTransportClient.connectOrThrow(any())).thenReturn(mBackupTransport); + when(mBackupTransport.getTransportFlags()).thenReturn(0); + + int operationType = mService.getOperationTypeFromTransport(mTransportClient); + + assertThat(operationType).isEqualTo(OperationType.BACKUP); + } + + @Test + public void testGetOperationTypeFromTransport_returnsMigrationForMigrationTransport() + throws Exception { + // This is a temporary flag to control the new behaviour until it's ready to be fully + // rolled out. + mService.shouldUseNewBackupEligibilityRules = true; + + when(mTransportClient.connectOrThrow(any())).thenReturn(mBackupTransport); + when(mBackupTransport.getTransportFlags()).thenReturn( + BackupAgent.FLAG_DEVICE_TO_DEVICE_TRANSFER); + + int operationType = mService.getOperationTypeFromTransport(mTransportClient); + + assertThat(operationType).isEqualTo(OperationType.MIGRATION); + } + private static PackageInfo getPackageInfo(String packageName) { PackageInfo packageInfo = new PackageInfo(); packageInfo.applicationInfo = new ApplicationInfo(); @@ -141,6 +171,7 @@ public class UserBackupManagerServiceTest { private static class TestBackupService extends UserBackupManagerService { boolean isEnabledStatePersisted = false; + boolean shouldUseNewBackupEligibilityRules = false; TestBackupService(Context context, PackageManager packageManager) { super(context, packageManager); @@ -158,5 +189,10 @@ public class UserBackupManagerServiceTest { @Override void updateStateOnBackupEnabled(boolean wasEnabled, boolean enable) {} + + @Override + boolean shouldUseNewBackupEligibilityRules() { + return shouldUseNewBackupEligibilityRules; + } } } diff --git a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java index 2cbc3f381909..a694d5e37566 100644 --- a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java +++ b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java @@ -153,4 +153,25 @@ public class SyncOperationTest extends AndroidTestCase { assertEquals("Period not restored", periodic.periodMillis, oneoff.periodMillis); assertEquals("Flex not restored", periodic.flexMillis, oneoff.flexMillis); } + + @SmallTest + public void testScheduleAsEjIsInExtras() { + Account account1 = new Account("account1", "type1"); + Bundle b1 = new Bundle(); + b1.putBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, true); + + SyncOperation op1 = new SyncOperation(account1, 0, 1, "foo", 0, + SyncOperation.REASON_USER_START, "authority1", b1, false, + ContentResolver.SYNC_EXEMPTION_NONE); + assertTrue(op1.isScheduledAsExpeditedJob()); + + PersistableBundle pb = op1.toJobInfoExtras(); + assertTrue("EJ extra not found in job extras", + ((PersistableBundle) pb.get("syncExtras")) + .containsKey(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB)); + + SyncOperation op2 = SyncOperation.maybeCreateFromJobExtras(pb); + assertTrue("EJ extra not found in extras", op2.getClonedExtras() + .getBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB)); + } } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 6add8d18aa3e..87100a63e35e 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -20,6 +20,8 @@ import static android.app.Notification.EXTRA_TITLE; import static android.app.admin.DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE; import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS; import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO; import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI; import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID; @@ -826,7 +828,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { * {@link DevicePolicyManager#forceRemoveActiveAdmin(ComponentName, int)} */ @Test - public void testForceRemoveActiveAdmin() throws Exception { + public void testForceRemoveActiveAdmin_nonShellCaller() throws Exception { mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); // Add admin. @@ -840,8 +842,53 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Calling from a non-shell uid should fail with a SecurityException mContext.binder.callingUid = 123456; assertExpectException(SecurityException.class, - /* messageRegex =*/ "Non-shell user attempted to call", + /* messageRegex = */ null, () -> dpms.forceRemoveActiveAdmin(admin1, CALLER_USER_HANDLE)); + } + + /** + * Test for: + * {@link DevicePolicyManager#forceRemoveActiveAdmin(ComponentName, int)} + */ + @Test + public void testForceRemoveActiveAdmin_nonShellCallerWithPermission() throws Exception { + mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); + + // Add admin. + setupPackageInPackageManager(admin1.getPackageName(), + /* userId= */ CALLER_USER_HANDLE, + /* appId= */ 10138, + /* flags= */ ApplicationInfo.FLAG_TEST_ONLY); + dpm.setActiveAdmin(admin1, /* replace =*/ false); + assertThat(dpm.isAdminActive(admin1)).isTrue(); + + mContext.binder.callingUid = 123456; + mContext.callerPermissions.add( + android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); + dpms.forceRemoveActiveAdmin(admin1, CALLER_USER_HANDLE); + + mContext.callerPermissions.add(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); + // Verify + assertThat(dpm.isAdminActiveAsUser(admin1, CALLER_USER_HANDLE)).isFalse(); + verify(getServices().usageStatsManagerInternal).setActiveAdminApps( + null, CALLER_USER_HANDLE); + } + + /** + * Test for: + * {@link DevicePolicyManager#forceRemoveActiveAdmin(ComponentName, int)} + */ + @Test + public void testForceRemoveActiveAdmin_ShellCaller() throws Exception { + mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); + + // Add admin. + setupPackageInPackageManager(admin1.getPackageName(), + /* userId= */ CALLER_USER_HANDLE, + /* appId= */ 10138, + /* flags= */ ApplicationInfo.FLAG_TEST_ONLY); + dpm.setActiveAdmin(admin1, /* replace =*/ false); + assertThat(dpm.isAdminActive(admin1)).isTrue(); mContext.binder.callingUid = Process.SHELL_UID; dpms.forceRemoveActiveAdmin(admin1, CALLER_USER_HANDLE); @@ -7028,6 +7075,71 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + public void testSetDeviceOwnerType_throwsExceptionWhenCallerNotAuthorized() { + assertThrows(SecurityException.class, + () -> dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_DEFAULT)); + } + + @Test + public void testSetDeviceOwnerType_throwsExceptionWhenThereIsNoDeviceOwner() { + mContext.binder.clearCallingIdentity(); + assertThrows(IllegalStateException.class, + () -> dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_DEFAULT)); + } + + @Test + public void testSetDeviceOwnerType_throwsExceptionWhenNotAsDeviceOwnerAdmin() throws Exception { + setDeviceOwner(); + + assertThrows(IllegalStateException.class, + () -> dpm.setDeviceOwnerType(admin2, DEVICE_OWNER_TYPE_FINANCED)); + } + + @Test + public void testSetDeviceOwnerType_asDeviceOwner_toFinancedDevice() throws Exception { + setDeviceOwner(); + + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + int returnedDeviceOwnerType = dpm.getDeviceOwnerType(admin1); + assertThat(dpms.mOwners.hasDeviceOwner()).isTrue(); + assertThat(returnedDeviceOwnerType).isEqualTo(DEVICE_OWNER_TYPE_FINANCED); + + initializeDpms(); + + returnedDeviceOwnerType = dpm.getDeviceOwnerType(admin1); + assertThat(dpms.mOwners.hasDeviceOwner()).isTrue(); + assertThat(returnedDeviceOwnerType).isEqualTo(DEVICE_OWNER_TYPE_FINANCED); + } + + @Test + public void testSetDeviceOwnerType_asDeviceOwner_throwsExceptionWhenSetDeviceOwnerTypeAgain() + throws Exception { + setDeviceOwner(); + + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + int returnedDeviceOwnerType = dpm.getDeviceOwnerType(admin1); + assertThat(dpms.mOwners.hasDeviceOwner()).isTrue(); + assertThat(returnedDeviceOwnerType).isEqualTo(DEVICE_OWNER_TYPE_FINANCED); + + assertThrows(IllegalStateException.class, + () -> dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_DEFAULT)); + } + + @Test + public void testGetDeviceOwnerType_throwsExceptionWhenThereIsNoDeviceOwner() { + assertThrows(IllegalStateException.class, () -> dpm.getDeviceOwnerType(admin1)); + } + + @Test + public void testGetDeviceOwnerType_throwsExceptionWhenNotAsDeviceOwnerAdmin() throws Exception { + setDeviceOwner(); + + assertThrows(IllegalStateException.class, () -> dpm.getDeviceOwnerType(admin2)); + } + + @Test public void testSetUsbDataSignalingEnabled_noDeviceOwnerOrPoOfOrgOwnedDevice() { assertThrows(SecurityException.class, () -> dpm.setUsbDataSignalingEnabled(true)); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java index bfe183cc608b..39ca925d0115 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java @@ -16,6 +16,9 @@ package com.android.server.devicepolicy; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; + import static com.google.common.truth.Truth.assertThat; import android.content.ComponentName; @@ -35,7 +38,6 @@ import org.junit.runner.RunWith; * <p>Run this test with: * * {@code atest FrameworksServicesTests:com.android.server.devicepolicy.OwnersTest} - * */ @SmallTest @RunWith(AndroidJUnit4.class) @@ -67,6 +69,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.hasDeviceOwner()).isFalse(); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getSystemUpdatePolicy()).isNull(); assertThat(owners.getProfileOwnerKeys()).isEmpty(); @@ -75,6 +79,12 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse(); assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse(); assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse(); + + owners.setDeviceOwnerType(owners.getDeviceOwnerPackageName(), + DEVICE_OWNER_TYPE_FINANCED); + // There is no device owner, so the default owner type should be returned. + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); } // Then re-read and check. @@ -84,6 +94,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.hasDeviceOwner()).isFalse(); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getSystemUpdatePolicy()).isNull(); assertThat(owners.getProfileOwnerKeys()).isEmpty(); @@ -122,6 +134,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.getDeviceOwnerName()).isEqualTo(null); assertThat(owners.getDeviceOwnerPackageName()).isEqualTo("com.google.android.testdpc"); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getSystemUpdatePolicy()).isNull(); assertThat(owners.getProfileOwnerKeys()).isEmpty(); @@ -142,6 +156,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.getDeviceOwnerName()).isEqualTo(null); assertThat(owners.getDeviceOwnerPackageName()).isEqualTo("com.google.android.testdpc"); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getSystemUpdatePolicy()).isNull(); assertThat(owners.getProfileOwnerKeys()).isEmpty(); @@ -180,6 +196,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.hasDeviceOwner()).isFalse(); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); assertThat(owners.getSystemUpdatePolicy()).isNull(); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getProfileOwnerKeys()).hasSize(2); assertThat(owners.getProfileOwnerComponent(10)) @@ -208,6 +226,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.hasDeviceOwner()).isFalse(); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); assertThat(owners.getSystemUpdatePolicy()).isNull(); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getProfileOwnerKeys()).hasSize(2); assertThat(owners.getProfileOwnerComponent(10)) @@ -260,6 +280,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.getDeviceOwnerName()).isEqualTo(null); assertThat(owners.getDeviceOwnerPackageName()).isEqualTo("com.google.android.testdpc"); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getSystemUpdatePolicy()).isNotNull(); assertThat(owners.getSystemUpdatePolicy().getPolicyType()).isEqualTo(5); @@ -292,6 +314,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.getDeviceOwnerName()).isEqualTo(null); assertThat(owners.getDeviceOwnerPackageName()).isEqualTo("com.google.android.testdpc"); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getSystemUpdatePolicy()).isNotNull(); assertThat(owners.getSystemUpdatePolicy().getPolicyType()).isEqualTo(5); @@ -315,12 +339,21 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse(); owners.setDeviceOwnerUserRestrictionsMigrated(); + + owners.setDeviceOwnerType(owners.getDeviceOwnerPackageName(), + DEVICE_OWNER_TYPE_FINANCED); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_FINANCED); } { final OwnersTestable owners = new OwnersTestable(getServices()); owners.load(); + assertThat(owners.hasDeviceOwner()).isTrue(); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_FINANCED); + assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse(); assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isTrue(); assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isTrue(); @@ -328,12 +361,22 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse(); owners.setProfileOwnerUserRestrictionsMigrated(11); + + owners.setDeviceOwnerType(owners.getDeviceOwnerPackageName(), + DEVICE_OWNER_TYPE_DEFAULT); + // The previous device owner type should persist. + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_FINANCED); } { final OwnersTestable owners = new OwnersTestable(getServices()); owners.load(); + assertThat(owners.hasDeviceOwner()).isTrue(); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_FINANCED); + assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse(); assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isTrue(); assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse(); @@ -369,6 +412,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.hasDeviceOwner()).isFalse(); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getSystemUpdatePolicy()).isNull(); @@ -388,6 +433,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.hasDeviceOwner()).isFalse(); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getSystemUpdatePolicy()).isNull(); @@ -425,6 +472,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.hasDeviceOwner()).isFalse(); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getProfileOwnerKeys()).isEmpty(); assertThat(owners.getSystemUpdatePolicy()).isNotNull(); @@ -444,6 +493,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.hasDeviceOwner()).isFalse(); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getProfileOwnerKeys()).isEmpty(); assertThat(owners.getSystemUpdatePolicy()).isNotNull(); @@ -472,9 +523,16 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.getLegacyConfigFile().exists()).isFalse(); assertThat(owners.getDeviceOwnerFile().exists()).isTrue(); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getProfileOwnerFile(10).exists()).isTrue(); assertThat(owners.getProfileOwnerFile(11).exists()).isTrue(); + String previousDeviceOwnerPackageName = owners.getDeviceOwnerPackageName(); + owners.setDeviceOwnerType(previousDeviceOwnerPackageName, DEVICE_OWNER_TYPE_FINANCED); + assertThat(owners.getDeviceOwnerType(previousDeviceOwnerPackageName)).isEqualTo( + DEVICE_OWNER_TYPE_FINANCED); + // Then clear all information and save. owners.clearDeviceOwner(); owners.clearSystemUpdatePolicy(); @@ -491,5 +549,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.getDeviceOwnerFile().exists()).isFalse(); assertThat(owners.getProfileOwnerFile(10).exists()).isFalse(); assertThat(owners.getProfileOwnerFile(11).exists()).isFalse(); + + assertThat(owners.getDeviceOwnerType(previousDeviceOwnerPackageName)).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); } } diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java index 1c55072cab2c..bc86d1d39b1c 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -993,7 +993,7 @@ public class DisplayManagerServiceTest { private DisplayDeviceInfo mDisplayDeviceInfo; FakeDisplayDevice() { - super(null, null, ""); + super(null, null, "", mContext); } public void setDisplayDeviceInfo(DisplayDeviceInfo displayDeviceInfo) { diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java index f49cbca12fd8..9bf95c0edcdb 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java @@ -221,7 +221,8 @@ public class DeviceSelectActionTest { "testDeviceSelect"); action.start(); mTestLooper.dispatchAll(); - assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SET_STREAM_PATH); + assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); + mNativeWrapper.clearResultMessages(); assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); action.processCommand(REPORT_POWER_STATUS_ON); mTestLooper.dispatchAll(); @@ -238,12 +239,15 @@ public class DeviceSelectActionTest { DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback, /*isCec20=*/false); action.start(); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); action.processCommand(REPORT_POWER_STATUS_STANDBY); mTestLooper.dispatchAll(); HdmiCecMessage userControlPressed = HdmiCecMessageBuilder.buildUserControlPressed( ADDR_TV, ADDR_PLAYBACK_1, HdmiCecKeycode.CEC_KEYCODE_POWER); assertThat(mNativeWrapper.getResultMessages()).contains(userControlPressed); + mNativeWrapper.clearResultMessages(); assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON); action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON); action.processCommand(REPORT_POWER_STATUS_ON); @@ -261,6 +265,9 @@ public class DeviceSelectActionTest { DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback, /*isCec20=*/false); action.start(); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); + mNativeWrapper.clearResultMessages(); assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); action.processCommand(REPORT_POWER_STATUS_STANDBY); assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON); @@ -285,6 +292,9 @@ public class DeviceSelectActionTest { DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback, /*isCec20=*/false); action.start(); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); + mNativeWrapper.clearResultMessages(); assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); action.processCommand(REPORT_POWER_STATUS_STANDBY); assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON); @@ -330,8 +340,11 @@ public class DeviceSelectActionTest { action.start(); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); + mNativeWrapper.clearResultMessages(); assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); action.processCommand(REPORT_POWER_STATUS_ON); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SET_STREAM_PATH); assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); } @@ -354,9 +367,12 @@ public class DeviceSelectActionTest { HdmiCecMessage userControlPressed = HdmiCecMessageBuilder.buildUserControlPressed( ADDR_TV, ADDR_PLAYBACK_1, HdmiCecKeycode.CEC_KEYCODE_POWER); assertThat(mNativeWrapper.getResultMessages()).doesNotContain(userControlPressed); + mNativeWrapper.clearResultMessages(); assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON); action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON); action.processCommand(REPORT_POWER_STATUS_ON); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SET_STREAM_PATH); assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java index 462f3e32ab3b..32a70480c39e 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -485,17 +485,6 @@ public class HdmiControlServiceTest { } @Test - public void getCecVersion_default() { - // Set the Settings value to "null" to emulate it being empty and force the default value. - Settings.Global.putString(mContextSpy.getContentResolver(), - Settings.Global.HDMI_CEC_VERSION, - null); - mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); - assertThat(mHdmiControlService.getCecVersion()).isEqualTo( - HdmiControlManager.HDMI_CEC_VERSION_1_4_B); - } - - @Test public void getCecVersion_1_4() { mHdmiControlService.getHdmiCecConfig().setIntValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, diff --git a/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java index 3e9709d55268..f0a9a0089ec9 100644 --- a/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java @@ -31,6 +31,7 @@ import android.hardware.light.ILights; import android.hardware.lights.Light; import android.hardware.lights.LightState; import android.hardware.lights.LightsManager; +import android.hardware.lights.SystemLightsManager; import android.os.Looper; import androidx.test.filters.SmallTest; @@ -93,7 +94,7 @@ public class LightsServiceTest { @Test public void testGetLights_filtersSystemLights() { LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper()); - LightsManager manager = new LightsManager(mContext, service.mManagerService); + LightsManager manager = new SystemLightsManager(mContext, service.mManagerService); // When lights are listed, only the 4 MICROPHONE lights should be visible. assertThat(manager.getLights().size()).isEqualTo(4); @@ -102,14 +103,14 @@ public class LightsServiceTest { @Test public void testControlMultipleLights() { LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper()); - LightsManager manager = new LightsManager(mContext, service.mManagerService); + LightsManager manager = new SystemLightsManager(mContext, service.mManagerService); // When the session requests to turn 3/4 lights on: LightsManager.LightsSession session = manager.openSession(); session.requestLights(new Builder() - .setLight(manager.getLights().get(0), new LightState(0xf1)) - .setLight(manager.getLights().get(1), new LightState(0xf2)) - .setLight(manager.getLights().get(2), new LightState(0xf3)) + .addLight(manager.getLights().get(0), new LightState(0xf1)) + .addLight(manager.getLights().get(1), new LightState(0xf2)) + .addLight(manager.getLights().get(2), new LightState(0xf3)) .build()); // Then all 3 should turn on. @@ -122,9 +123,9 @@ public class LightsServiceTest { } @Test - public void testControlLights_onlyEffectiveForLifetimeOfClient() { + public void testControlLights_onlyEffectiveForLifetimeOfClient() throws Exception { LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper()); - LightsManager manager = new LightsManager(mContext, service.mManagerService); + LightsManager manager = new SystemLightsManager(mContext, service.mManagerService); Light micLight = manager.getLights().get(0); // The light should begin by being off. @@ -132,38 +133,41 @@ public class LightsServiceTest { // When a session commits changes: LightsManager.LightsSession session = manager.openSession(); - session.requestLights(new Builder().setLight(micLight, new LightState(GREEN)).build()); + session.requestLights(new Builder().addLight(micLight, new LightState(GREEN)).build()); // Then the light should turn on. assertThat(manager.getLightState(micLight).getColor()).isEqualTo(GREEN); // When the session goes away: session.close(); + // Then the light should turn off. assertThat(manager.getLightState(micLight).getColor()).isEqualTo(TRANSPARENT); } @Test - public void testControlLights_firstCallerWinsContention() { + public void testControlLights_firstCallerWinsContention() throws Exception { LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper()); - LightsManager manager = new LightsManager(mContext, service.mManagerService); + LightsManager manager = new SystemLightsManager(mContext, service.mManagerService); Light micLight = manager.getLights().get(0); LightsManager.LightsSession session1 = manager.openSession(); LightsManager.LightsSession session2 = manager.openSession(); // When session1 and session2 both request the same light: - session1.requestLights(new Builder().setLight(micLight, new LightState(BLUE)).build()); - session2.requestLights(new Builder().setLight(micLight, new LightState(WHITE)).build()); + session1.requestLights(new Builder().addLight(micLight, new LightState(BLUE)).build()); + session2.requestLights(new Builder().addLight(micLight, new LightState(WHITE)).build()); // Then session1 should win because it was created first. assertThat(manager.getLightState(micLight).getColor()).isEqualTo(BLUE); // When session1 goes away: session1.close(); + // Then session2 should have its request go into effect. assertThat(manager.getLightState(micLight).getColor()).isEqualTo(WHITE); // When session2 goes away: session2.close(); + // Then the light should turn off because there are no more sessions. assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0); } @@ -171,12 +175,12 @@ public class LightsServiceTest { @Test public void testClearLight() { LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper()); - LightsManager manager = new LightsManager(mContext, service.mManagerService); + LightsManager manager = new SystemLightsManager(mContext, service.mManagerService); Light micLight = manager.getLights().get(0); // When the session turns a light on: LightsManager.LightsSession session = manager.openSession(); - session.requestLights(new Builder().setLight(micLight, new LightState(WHITE)).build()); + session.requestLights(new Builder().addLight(micLight, new LightState(WHITE)).build()); // And then the session clears it again: session.requestLights(new Builder().clearLight(micLight).build()); diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index 7d7af03ecd3d..74bf4f5da70d 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -115,7 +115,6 @@ import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkPolicy; -import android.net.NetworkPolicyManager; import android.net.NetworkState; import android.net.NetworkStats; import android.net.NetworkStatsHistory; @@ -386,8 +385,7 @@ public class NetworkPolicyManagerServiceTest { Log.d(TAG, "set mUidObserver to " + mUidObserver); return null; } - }).when(mActivityManager).registerUidObserver(any(), anyInt(), - eq(NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE), any(String.class)); + }).when(mActivityManager).registerUidObserver(any(), anyInt(), anyInt(), any(String.class)); mFutureIntent = newRestrictBackgroundChangedFuture(); mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager, diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java index ff43da6370e8..ee0a16a70265 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java @@ -86,6 +86,7 @@ public class DexManagerTests { private TestData mBarUser0DelegateLastClassLoader; private TestData mSystemServerJar; + private TestData mSystemServerJarUpdatedContext; private TestData mSystemServerJarInvalid; private int mUser0; @@ -113,6 +114,8 @@ public class DexManagerTests { mSystemServerJar = new TestData("android", isa, mUser0, PATH_CLASS_LOADER_NAME); mSystemServerJarInvalid = new TestData("android", isa, mUser0, PATH_CLASS_LOADER_NAME); + mSystemServerJarUpdatedContext = new TestData("android", isa, mUser0, + DELEGATE_LAST_CLASS_LOADER_NAME); mDexManager = new DexManager(/*Context*/ null, mPM, /*PackageDexOptimizer*/ null, mInstaller, mInstallLock); @@ -522,6 +525,24 @@ public class DexManagerTests { } @Test + public void testSystemServerOverwritesContext() { + // Record bar secondaries with the default PathClassLoader. + List<String> secondaries = mSystemServerJar.getSecondaryDexPaths(); + + notifyDexLoad(mSystemServerJar, secondaries, mUser0); + PackageUseInfo pui = getPackageUseInfo(mSystemServerJar); + assertSecondaryUse(mSystemServerJar, pui, secondaries, /*isUsedByOtherApps*/false, mUser0); + + // Record bar secondaries again with a different class loader. This will change the context. + notifyDexLoad(mSystemServerJarUpdatedContext, secondaries, mUser0); + + pui = getPackageUseInfo(mSystemServerJar); + // We expect that all the contexts to be updated according to the last notify. + assertSecondaryUse(mSystemServerJarUpdatedContext, pui, secondaries, + /*isUsedByOtherApps*/false, mUser0); + } + + @Test public void testNotifyUnsupportedClassLoaderDoesNotChangeExisting() { List<String> secondaries = mBarUser0.getSecondaryDexPaths(); diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/OWNERS b/services/tests/servicestests/src/com/android/server/pm/dex/OWNERS new file mode 100644 index 000000000000..66ef75d6c823 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/dex/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/pm/dex/OWNERS diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java index adf4551e79a8..3450710f60a0 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java @@ -451,7 +451,7 @@ public class PackageDexUsageTests { "PCL[new_context.dex]"); assertTrue(record(fooSecondary1User0NewContext)); - // Not check that the context was switch to variable. + // Now check that the context was switch to variable. TestData expectedContext = mFooSecondary1User0.updateClassLoaderContext( PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT); @@ -461,6 +461,22 @@ public class PackageDexUsageTests { } @Test + public void testRecordClassLoaderContextOverwritten() { + // Record a secondary dex file. + assertTrue(record(mFooSecondary1User0)); + // Now update its context. + TestData fooSecondary1User0NewContext = mFooSecondary1User0.updateClassLoaderContext( + "PCL[new_context.dex]", true); + assertTrue(record(fooSecondary1User0NewContext)); + + // Now check that the context was overwritten. + TestData expectedContext = mFooSecondary1User0.updateClassLoaderContext( + "PCL[new_context.dex]", true); + + assertPackageDexUsage(null, expectedContext); + } + + @Test public void testDexUsageClassLoaderContext() { final boolean isUsedByOtherApps = false; final int userId = 0; @@ -642,8 +658,9 @@ public class PackageDexUsageTests { private boolean record(TestData testData) { return mPackageDexUsage.record(testData.mPackageName, testData.mDexFile, - testData.mOwnerUserId, testData.mLoaderIsa, - testData.mPrimaryOrSplit, testData.mUsedBy, testData.mClassLoaderContext); + testData.mOwnerUserId, testData.mLoaderIsa, + testData.mPrimaryOrSplit, testData.mUsedBy, testData.mClassLoaderContext, + testData.mOverwriteCLC); } private boolean record(PackageDexUsage packageDexUsage, TestData testData, Set<String> users) { @@ -651,7 +668,8 @@ public class PackageDexUsageTests { for (String user : users) { result = result && packageDexUsage.record(testData.mPackageName, testData.mDexFile, testData.mOwnerUserId, testData.mLoaderIsa, - testData.mPrimaryOrSplit, user, testData.mClassLoaderContext); + testData.mPrimaryOrSplit, user, testData.mClassLoaderContext, + testData.mOverwriteCLC); } return result; } @@ -682,15 +700,16 @@ public class PackageDexUsageTests { private final boolean mPrimaryOrSplit; private final String mUsedBy; private final String mClassLoaderContext; + private final boolean mOverwriteCLC; private TestData(String packageName, String dexFile, int ownerUserId, String loaderIsa, boolean primaryOrSplit, String usedBy) { this(packageName, dexFile, ownerUserId, loaderIsa, primaryOrSplit, - usedBy, "PCL[" + dexFile + "]"); + usedBy, "PCL[" + dexFile + "]", false); } private TestData(String packageName, String dexFile, int ownerUserId, String loaderIsa, boolean primaryOrSplit, String usedBy, - String classLoaderContext) { + String classLoaderContext, boolean overwriteCLC) { mPackageName = packageName; mDexFile = dexFile; mOwnerUserId = ownerUserId; @@ -698,16 +717,21 @@ public class PackageDexUsageTests { mPrimaryOrSplit = primaryOrSplit; mUsedBy = usedBy; mClassLoaderContext = classLoaderContext; + mOverwriteCLC = overwriteCLC; } private TestData updateClassLoaderContext(String newContext) { + return updateClassLoaderContext(newContext, mOverwriteCLC); + } + + private TestData updateClassLoaderContext(String newContext, boolean overwriteCLC) { return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa, - mPrimaryOrSplit, mUsedBy, newContext); + mPrimaryOrSplit, mUsedBy, newContext, overwriteCLC); } private TestData updateUsedBy(String newUsedBy) { return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa, - mPrimaryOrSplit, newUsedBy, mClassLoaderContext); + mPrimaryOrSplit, newUsedBy, mClassLoaderContext, mOverwriteCLC); } private boolean isUsedByOtherApps() { 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 6b95cfc934ac..ea27331ac4ca 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -98,9 +98,11 @@ import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; /** * Tests for {@link com.android.server.power.PowerManagerService}. @@ -402,30 +404,33 @@ public class PowerManagerServiceTest { @Test public void testGetDesiredScreenPolicy_WithVR() throws Exception { createService(); + mService.systemReady(null); // Brighten up the screen - mService.setWakefulnessLocked(WAKEFULNESS_AWAKE, PowerManager.WAKE_REASON_UNKNOWN, 0); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_AWAKE, 0, 0, 0, 0, + null, null); + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_BRIGHT); // Move to VR mService.setVrModeEnabled(true); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_VR); // Then take a nap - mService.setWakefulnessLocked(WAKEFULNESS_ASLEEP, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, - 0); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_ASLEEP, 0, 0, 0, 0, + null, null); + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_OFF); // Wake up to VR - mService.setWakefulnessLocked(WAKEFULNESS_AWAKE, PowerManager.WAKE_REASON_UNKNOWN, 0); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_AWAKE, 0, 0, 0, 0, + null, null); + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_VR); // And back to normal mService.setVrModeEnabled(false); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_BRIGHT); } @@ -676,8 +681,9 @@ public class PowerManagerServiceTest { } @Test - public void testForceSuspend_forceSuspendFailurePropogated() { + public void testForceSuspend_forceSuspendFailurePropagated() throws Exception { createService(); + startSystem(); when(mNativeWrapperMock.nativeForceSuspend()).thenReturn(false); assertThat(mService.getBinderServiceInstance().forceSuspend()).isFalse(); } @@ -841,7 +847,7 @@ public class PowerManagerServiceTest { createService(); startSystem(); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_BRIGHT); } @@ -860,11 +866,8 @@ public class PowerManagerServiceTest { public void testQuiescentBoot_DesiredScreenPolicyShouldBeOff() throws Exception { when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1"); createService(); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( - DisplayPowerRequest.POLICY_OFF); - startSystem(); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_OFF); } @@ -874,7 +877,7 @@ public class PowerManagerServiceTest { createService(); startSystem(); forceAwake(); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_BRIGHT); } @@ -890,7 +893,7 @@ public class PowerManagerServiceTest { mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo( DisplayPowerRequest.POLICY_BRIGHT); } @@ -1164,6 +1167,87 @@ public class PowerManagerServiceTest { } @Test + public void testMultiDisplay_wakefulnessUpdates() throws Exception { + final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1; + final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener = + new AtomicReference<>(); + doAnswer((Answer<Void>) invocation -> { + listener.set(invocation.getArgument(0)); + return null; + }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any()); + + createService(); + startSystem(); + listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId); + + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + + mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_ASLEEP, 0, 0, 0, 0, + null, null); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + + mService.setWakefulnessLocked(nonDefaultDisplayGroupId, WAKEFULNESS_ASLEEP, 0, 0, 0, 0, + null, null); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); + + mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_AWAKE, 0, 0, 0, 0, + null, null); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + } + + @Test + public void testMultiDisplay_addDisplayGroup_preservesWakefulness() throws Exception { + final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1; + final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener = + new AtomicReference<>(); + doAnswer((Answer<Void>) invocation -> { + listener.set(invocation.getArgument(0)); + return null; + }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any()); + + createService(); + startSystem(); + + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + + mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_ASLEEP, 0, 0, 0, 0, + null, null); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); + + listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId); + + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); + } + + @Test + public void testMultiDisplay_removeDisplayGroup_updatesWakefulness() throws Exception { + final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1; + final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener = + new AtomicReference<>(); + doAnswer((Answer<Void>) invocation -> { + listener.set(invocation.getArgument(0)); + return null; + }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any()); + + createService(); + startSystem(); + listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId); + + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + + mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_ASLEEP, 0, 0, 0, 0, + null, null); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + + listener.get().onDisplayGroupRemoved(nonDefaultDisplayGroupId); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); + + mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_AWAKE, 0, 0, 0, 0, + null, null); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + } + + @Test public void testGetFullPowerSavePolicy_returnsStateMachineResult() { createService(); mService.systemReady(null); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index a64050996d42..cebdbbef2329 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -3671,8 +3671,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(r); final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 0, 1, true); - mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.getSbn().getTag(), - r.getSbn().getId(), r.getUserId(), r.getKey(), NotificationStats.DISMISSAL_AOD, + mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.getUserId(), + r.getKey(), NotificationStats.DISMISSAL_AOD, NotificationStats.DISMISS_SENTIMENT_POSITIVE, nv); waitForIdle(); @@ -3694,8 +3694,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(r); final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 0, 1, true); - mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.getSbn().getTag(), - r.getSbn().getId(), r.getUserId(), r.getKey(), NotificationStats.DISMISSAL_AOD, + mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.getUserId(), + r.getKey(), NotificationStats.DISMISSAL_AOD, NotificationStats.DISMISS_SENTIMENT_NEGATIVE, nv); waitForIdle(); @@ -6693,8 +6693,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final NotificationVisibility nv = NotificationVisibility.obtain(nrSummary.getKey(), 1, 2, true); mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, - nrSummary.getSbn().getTag(), - nrSummary.getSbn().getId(), nrSummary.getUserId(), nrSummary.getKey(), + nrSummary.getUserId(), nrSummary.getKey(), NotificationStats.DISMISSAL_SHADE, NotificationStats.DISMISS_SENTIMENT_NEUTRAL, nv); waitForIdle(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java index 35b224a24061..2ae2ef7162a5 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java @@ -51,6 +51,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.FastXmlSerializer; import com.android.server.UiServiceTestCase; +import com.android.server.pm.PackageManagerService; import org.junit.Before; import org.junit.Test; @@ -259,6 +260,17 @@ public class SnoozeHelperTest extends UiServiceTestCase { } @Test + public void testSnoozeSentToAndroid() throws Exception { + NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); + mSnoozeHelper.snooze(r, 1000); + ArgumentCaptor<PendingIntent> captor = ArgumentCaptor.forClass(PendingIntent.class); + verify(mAm, times(1)).setExactAndAllowWhileIdle( + anyInt(), anyLong(), captor.capture()); + assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME, + captor.getValue().getIntent().getPackage()); + } + + @Test public void testSnooze() throws Exception { NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); mSnoozeHelper.snooze(r, (String) null); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index aa1110cd55a7..488e629f5790 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -2288,7 +2288,7 @@ public class ActivityRecordTests extends WindowTestsBase { IWindowManager.FIXED_TO_USER_ROTATION_ENABLED); reset(task); activity.reportDescendantOrientationChangeIfNeeded(); - verify(task).onConfigurationChanged(any(Configuration.class)); + verify(task, atLeast(1)).onConfigurationChanged(any(Configuration.class)); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java index d13e4dcaf9fd..7df17fd4b3c6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java @@ -38,6 +38,7 @@ import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS; import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature; +import static com.android.server.wm.DisplayAreaPolicyBuilder.KEY_ROOT_DISPLAY_AREA_ID; import static com.google.common.truth.Truth.assertThat; @@ -595,6 +596,17 @@ public class DisplayAreaPolicyBuilderTest { assertThat(token.isDescendantOf(mRoot)).isTrue(); assertThat(token.isDescendantOf(mGroupRoot1)).isFalse(); assertThat(token.isDescendantOf(mGroupRoot2)).isFalse(); + + // When the window has options for target root id, attach it to the target root. + final Bundle options = new Bundle(); + options.putInt(KEY_ROOT_DISPLAY_AREA_ID, mGroupRoot2.mFeatureId); + final WindowToken token2 = new WindowToken(mWms, mock(IBinder.class), + TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, + true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */, + false /* fromClientToken */, options); + policy.addWindow(token2); + + assertThat(token2.isDescendantOf(mGroupRoot2)).isTrue(); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 58169bb1e073..137cf6523caf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -32,6 +32,8 @@ import static android.view.DisplayCutout.fromBoundingRect; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_180; +import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; @@ -121,6 +123,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; +import com.android.server.policy.WindowManagerPolicy; import com.android.server.wm.utils.WmDisplayCutout; import org.junit.Test; @@ -955,16 +958,14 @@ public class DisplayContentTests extends WindowTestsBase { IWindowManager.FIXED_TO_USER_ROTATION_DISABLED); final int newOrientation = getRotatedOrientation(dc); - final Task stack = new TaskBuilder(mSupervisor) + final Task task = new TaskBuilder(mSupervisor) .setDisplay(dc).setCreateActivity(true).build(); - final ActivityRecord activity = stack.getTopMostTask().getTopNonFinishingActivity(); + final ActivityRecord activity = task.getTopMostTask().getTopNonFinishingActivity(); + dc.setFocusedApp(activity); activity.setRequestedOrientation(newOrientation); - final int expectedOrientation = newOrientation == SCREEN_ORIENTATION_PORTRAIT - ? Configuration.ORIENTATION_PORTRAIT - : Configuration.ORIENTATION_LANDSCAPE; - assertEquals(expectedOrientation, dc.getConfiguration().orientation); + assertTrue("The display should be rotated.", dc.getRotation() % 2 == 1); } @Test @@ -972,17 +973,42 @@ public class DisplayContentTests extends WindowTestsBase { final DisplayContent dc = createNewDisplay(); dc.getDisplayRotation().setFixedToUserRotation( IWindowManager.FIXED_TO_USER_ROTATION_ENABLED); + dc.getDisplayRotation().setUserRotation( + WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_180); final int newOrientation = getRotatedOrientation(dc); - final Task stack = new TaskBuilder(mSupervisor) + final Task task = new TaskBuilder(mSupervisor) .setDisplay(dc).setCreateActivity(true).build(); - final ActivityRecord activity = stack.getTopMostTask().getTopNonFinishingActivity(); + final ActivityRecord activity = task.getTopMostTask().getTopNonFinishingActivity(); + dc.setFocusedApp(activity); activity.setRequestedOrientation(newOrientation); verify(dc, never()).updateDisplayOverrideConfigurationLocked(any(), eq(activity), anyBoolean(), same(null)); - assertEquals(dc.getDisplayRotation().getUserRotation(), dc.getRotation()); + assertEquals(ROTATION_180, dc.getRotation()); + } + + @Test + public void testFixedToUserRotationChanged() { + final DisplayContent dc = createNewDisplay(); + dc.getDisplayRotation().setFixedToUserRotation( + IWindowManager.FIXED_TO_USER_ROTATION_ENABLED); + dc.getDisplayRotation().setUserRotation( + WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_0); + final int newOrientation = getRotatedOrientation(dc); + + final Task task = new TaskBuilder(mSupervisor) + .setDisplay(dc).setCreateActivity(true).build(); + final ActivityRecord activity = task.getTopMostTask().getTopNonFinishingActivity(); + dc.setFocusedApp(activity); + + activity.setRequestedOrientation(newOrientation); + + dc.getDisplayRotation().setFixedToUserRotation( + IWindowManager.FIXED_TO_USER_ROTATION_DISABLED); + + assertTrue("The display should be rotated.", dc.getRotation() % 2 == 1); } @Test @@ -1418,7 +1444,7 @@ public class DisplayContentTests extends WindowTestsBase { // Assume the animation of PipTaskOrganizer is done and then commit fullscreen to task. pinnedTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); displayContent.continueUpdateOrientationForDiffOrienLaunchingApp(); - assertFalse(displayContent.getPinnedStackController().isPipActiveOrWindowingModeChanging()); + assertFalse(displayContent.getPinnedTaskController().isPipActiveOrWindowingModeChanging()); assertEquals(pinnedConfigOrientation, displayConfig.orientation); clearInvocations(mWm); @@ -1429,7 +1455,7 @@ public class DisplayContentTests extends WindowTestsBase { assertFalse(displayContent.hasTopFixedRotationLaunchingApp()); verify(mWm, atLeastOnce()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt()); assertEquals(homeConfigOrientation, displayConfig.orientation); - assertTrue(displayContent.getPinnedStackController().isPipActiveOrWindowingModeChanging()); + assertTrue(displayContent.getPinnedTaskController().isPipActiveOrWindowingModeChanging()); } @Test @@ -1820,6 +1846,37 @@ public class DisplayContentTests extends WindowTestsBase { verify(t).show(mDisplayContent.mImeScreenshot); } + @Test + public void testRotateBounds_keepSamePhysicalPosition() { + final DisplayContent dc = + new TestDisplayContent.Builder(mAtm, 1000, 2000).build(); + final Rect initBounds = new Rect(0, 0, 700, 1500); + final Rect rotateBounds = new Rect(initBounds); + + // Rotate from 0 to 0 + dc.rotateBounds(ROTATION_0, ROTATION_0, rotateBounds); + + assertEquals(new Rect(0, 0, 700, 1500), rotateBounds); + + // Rotate from 0 to 90 + rotateBounds.set(initBounds); + dc.rotateBounds(ROTATION_0, ROTATION_90, rotateBounds); + + assertEquals(new Rect(0, 300, 1500, 1000), rotateBounds); + + // Rotate from 0 to 180 + rotateBounds.set(initBounds); + dc.rotateBounds(ROTATION_0, ROTATION_180, rotateBounds); + + assertEquals(new Rect(300, 500, 1000, 2000), rotateBounds); + + // Rotate from 0 to 270 + rotateBounds.set(initBounds); + dc.rotateBounds(ROTATION_0, ROTATION_270, rotateBounds); + + assertEquals(new Rect(500, 0, 2000, 700), rotateBounds); + } + private boolean isOptionsPanelAtRight(int displayId) { return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT; } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 896969548af3..51aec65f7285 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -251,7 +251,7 @@ public class WindowStateTests extends WindowTestsBase { // Simulate the window is in split screen primary stack and the current state is // minimized and home stack is resizable, so that we should ignore input for the stack. - final DockedStackDividerController controller = + final DockedTaskDividerController controller = mDisplayContent.getDockedDividerController(); final Task stack = createTaskStackOnDisplay(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDisplayContent); diff --git a/services/texttospeech/Android.bp b/services/texttospeech/Android.bp index bacc932f760f..391ab8946c2c 100644 --- a/services/texttospeech/Android.bp +++ b/services/texttospeech/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + filegroup { name: "services.texttospeech-sources", srcs: ["java/**/*.java"], @@ -10,4 +19,4 @@ java_library_static { defaults: ["platform_service_defaults"], srcs: [":services.texttospeech-sources"], libs: ["services.core"], -}
\ No newline at end of file +} diff --git a/services/translation/Android.bp b/services/translation/Android.bp index 804a6177e94e..f257f1b7339a 100644 --- a/services/translation/Android.bp +++ b/services/translation/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + filegroup { name: "services.translation-sources", srcs: ["java/**/*.java"], @@ -10,4 +19,4 @@ java_library_static { defaults: ["platform_service_defaults"], srcs: [":services.translation-sources"], libs: ["services.core"], -}
\ No newline at end of file +} diff --git a/telecomm/java/android/telecom/Connection.aidl b/telecomm/java/android/telecom/Connection.aidl new file mode 100644 index 000000000000..5b40036e46f4 --- /dev/null +++ b/telecomm/java/android/telecom/Connection.aidl @@ -0,0 +1,22 @@ +/* + * 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.telecom; + +/** + * {@hide} + */ +parcelable Connection.CallFilteringCompletionInfo; diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 942a54eb98ba..7c6253ce933a 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -29,6 +29,7 @@ import android.annotation.SystemApi; import android.app.Notification; import android.bluetooth.BluetoothDevice; import android.compat.annotation.UnsupportedAppUsage; +import android.content.ComponentName; import android.content.Intent; import android.hardware.camera2.CameraManager; import android.net.Uri; @@ -38,7 +39,9 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.Parcel; import android.os.ParcelFileDescriptor; +import android.os.Parcelable; import android.os.RemoteException; import android.os.SystemClock; import android.telephony.ims.ImsStreamMediaProfile; @@ -3379,6 +3382,121 @@ public abstract class Connection extends Conferenceable { public void handleRttUpgradeResponse(@Nullable RttTextStream rttTextStream) {} /** + * Information provided to a {@link Connection} upon completion of call filtering in Telecom. + * + * @hide + */ + @SystemApi + public static final class CallFilteringCompletionInfo implements Parcelable { + private final boolean mIsBlocked; + private final boolean mIsInContacts; + private final CallScreeningService.CallResponse mCallResponse; + private final ComponentName mCallScreeningComponent; + + /** + * Constructor for {@link CallFilteringCompletionInfo} + * + * @param isBlocked Whether any part of the call filtering process indicated that this call + * should be blocked. + * @param isInContacts Whether the caller is in the user's contacts. + * @param callResponse The instance of {@link CallScreeningService.CallResponse} provided + * by the {@link CallScreeningService} that processed this call, or + * {@code null} if no call screening service ran. + * @param callScreeningComponent The component of the {@link CallScreeningService} + * that processed this call, or {@link null} if no + * call screening service ran. + */ + public CallFilteringCompletionInfo(boolean isBlocked, boolean isInContacts, + @Nullable CallScreeningService.CallResponse callResponse, + @Nullable ComponentName callScreeningComponent) { + mIsBlocked = isBlocked; + mIsInContacts = isInContacts; + mCallResponse = callResponse; + mCallScreeningComponent = callScreeningComponent; + } + + /** @hide */ + protected CallFilteringCompletionInfo(Parcel in) { + mIsBlocked = in.readByte() != 0; + mIsInContacts = in.readByte() != 0; + CallScreeningService.ParcelableCallResponse response + = in.readParcelable(CallScreeningService.class.getClassLoader()); + mCallResponse = response == null ? null : response.toCallResponse(); + mCallScreeningComponent = in.readParcelable(ComponentName.class.getClassLoader()); + } + + @NonNull + public static final Creator<CallFilteringCompletionInfo> CREATOR = + new Creator<CallFilteringCompletionInfo>() { + @Override + public CallFilteringCompletionInfo createFromParcel(Parcel in) { + return new CallFilteringCompletionInfo(in); + } + + @Override + public CallFilteringCompletionInfo[] newArray(int size) { + return new CallFilteringCompletionInfo[size]; + } + }; + + /** + * @return Whether any part of the call filtering process indicated that this call should be + * blocked. + */ + public boolean isBlocked() { + return mIsBlocked; + } + + /** + * @return Whether the caller is in the user's contacts. + */ + public boolean isInContacts() { + return mIsInContacts; + } + + /** + * @return The instance of {@link CallScreeningService.CallResponse} provided + * by the {@link CallScreeningService} that processed this + * call, or {@code null} if no call screening service ran. + */ + public @Nullable CallScreeningService.CallResponse getCallResponse() { + return mCallResponse; + } + + /** + * @return The component of the {@link CallScreeningService} + * that processed this call, or {@code null} if no call screening service ran. + */ + public @Nullable ComponentName getCallScreeningComponent() { + return mCallScreeningComponent; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "CallFilteringCompletionInfo{" + + "mIsBlocked=" + mIsBlocked + + ", mIsInContacts=" + mIsInContacts + + ", mCallResponse=" + mCallResponse + + ", mCallScreeningPackageName='" + mCallScreeningComponent + '\'' + + '}'; + } + + /** @hide */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeByte((byte) (mIsBlocked ? 1 : 0)); + dest.writeByte((byte) (mIsInContacts ? 1 : 0)); + dest.writeParcelable(mCallResponse == null ? null : mCallResponse.toParcelable(), 0); + dest.writeParcelable(mCallScreeningComponent, 0); + } + } + + /** * Indicates that call filtering in Telecom is complete * * This method is called for a connection created via @@ -3386,24 +3504,13 @@ public abstract class Connection extends Conferenceable { * Telecom, including checking the blocked number db, per-contact settings, and custom call * filtering apps. * - * @param isBlocked {@code true} if the call was blocked, {@code false} otherwise. If this is - * {@code true}, {@link #onDisconnect()} will be called soon after - * this is called. - * @param isInContacts Indicates whether the caller is in the user's contacts list. - * @param callScreeningResponse The response that was returned from the - * {@link CallScreeningService} that handled this call. If no - * response was received from a call screening service, - * this will be {@code null}. - * @param isResponseFromSystemDialer Whether {@code callScreeningResponse} was sent from the - * system dialer. If {@code callScreeningResponse} is - * {@code null}, this will be {@code false}. + * @param callFilteringCompletionInfo Info provided by Telecom on the results of call filtering. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.READ_CONTACTS) - public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts, - @Nullable CallScreeningService.CallResponse callScreeningResponse, - boolean isResponseFromSystemDialer) { } + public void onCallFilteringCompleted( + @NonNull CallFilteringCompletionInfo callFilteringCompletionInfo) { } static String toLogSafePhoneNumber(String number) { // For unknown number, log empty string. diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 966ece3a3ba2..c189b19c71af 100755 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -758,19 +758,15 @@ public abstract class ConnectionService extends Service { } @Override - public void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts, - CallScreeningService.ParcelableCallResponse callScreeningResponse, - boolean isResponseFromSystemDialer, + public void onCallFilteringCompleted(String callId, + Connection.CallFilteringCompletionInfo completionInfo, Session.Info sessionInfo) { Log.startSession(sessionInfo, SESSION_CALL_FILTERING_COMPLETED); try { SomeArgs args = SomeArgs.obtain(); args.arg1 = callId; - args.arg2 = isBlocked; - args.arg3 = isInContacts; - args.arg4 = callScreeningResponse; - args.arg5 = isResponseFromSystemDialer; - args.arg6 = Log.createSubsession(); + args.arg2 = completionInfo; + args.arg3 = Log.createSubsession(); mHandler.obtainMessage(MSG_ON_CALL_FILTERING_COMPLETED, args).sendToTarget(); } finally { Log.endSession(); @@ -1441,16 +1437,12 @@ public abstract class ConnectionService extends Service { case MSG_ON_CALL_FILTERING_COMPLETED: { SomeArgs args = (SomeArgs) msg.obj; try { - Log.continueSession((Session) args.arg6, + Log.continueSession((Session) args.arg3, SESSION_HANDLER + SESSION_CALL_FILTERING_COMPLETED); String callId = (String) args.arg1; - boolean isBlocked = (boolean) args.arg2; - boolean isInContacts = (boolean) args.arg3; - CallScreeningService.ParcelableCallResponse callScreeningResponse = - (CallScreeningService.ParcelableCallResponse) args.arg4; - boolean isResponseFromSystemDialer = (boolean) args.arg5; - onCallFilteringCompleted(callId, isBlocked, isInContacts, - callScreeningResponse, isResponseFromSystemDialer); + Connection.CallFilteringCompletionInfo completionInfo = + (Connection.CallFilteringCompletionInfo) args.arg2; + onCallFilteringCompleted(callId, completionInfo); } finally { args.recycle(); Log.endSession(); @@ -2466,16 +2458,12 @@ public abstract class ConnectionService extends Service { } } - private void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts, - CallScreeningService.ParcelableCallResponse callScreeningResponse, - boolean isResponseFromSystemDialer) { - Log.i(this, "onCallFilteringCompleted(%s, %b, %b, %s, %b)", callId, - isBlocked, isInContacts, callScreeningResponse, isResponseFromSystemDialer); + private void onCallFilteringCompleted(String callId, Connection.CallFilteringCompletionInfo + callFilteringCompletionInfo) { + Log.i(this, "onCallFilteringCompleted(%s, %s)", callId, callFilteringCompletionInfo); Connection connection = findConnectionForAction(callId, "onCallFilteringCompleted"); if (connection != null) { - connection.onCallFilteringCompleted(isBlocked, isInContacts, - callScreeningResponse == null ? null : callScreeningResponse.toCallResponse(), - isResponseFromSystemDialer); + connection.onCallFilteringCompleted(callFilteringCompletionInfo); } } diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java index 6c6097ac71e5..7a6fddb6f029 100644 --- a/telecomm/java/android/telecom/RemoteConnection.java +++ b/telecomm/java/android/telecom/RemoteConnection.java @@ -1202,27 +1202,18 @@ public final class RemoteConnection { /** * Notifies this {@link RemoteConnection} that call filtering has completed, as well as * the results of a contacts lookup for the remote party. - * @param isBlocked Whether call filtering indicates that the call should be blocked - * @param isInContacts Whether the remote party is in the user's contacts - * @param callScreeningResponse The response that was returned from the - * {@link CallScreeningService} that handled this call. If no - * response was received from a call screening service, - * this will be {@code null}. - * @param isResponseFromSystemDialer Whether {@code callScreeningResponse} was sent from the - * system dialer. If {@code callScreeningResponse} is - * {@code null}, this will be {@code false}. + * + * @param completionInfo Info provided by Telecom on the results of call filtering. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.READ_CONTACTS) - public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts, - @Nullable CallScreeningService.CallResponse callScreeningResponse, - boolean isResponseFromSystemDialer) { + public void onCallFilteringCompleted( + @NonNull Connection.CallFilteringCompletionInfo completionInfo) { Log.startSession("RC.oCFC", getActiveOwnerInfo()); try { if (mConnected) { - mConnectionService.onCallFilteringCompleted(mConnectionId, isBlocked, isInContacts, - callScreeningResponse.toParcelable(), isResponseFromSystemDialer, + mConnectionService.onCallFilteringCompleted(mConnectionId, completionInfo, null /*Session.Info*/); } } catch (RemoteException ignored) { diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl index 7599e189cc37..d72f8aa82ddb 100644 --- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl +++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl @@ -20,7 +20,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.telecom.CallAudioState; -import android.telecom.CallScreeningService; +import android.telecom.Connection; import android.telecom.ConnectionRequest; import android.telecom.Logging.Session; import android.telecom.PhoneAccountHandle; @@ -119,9 +119,9 @@ oneway interface IConnectionService { void sendCallEvent(String callId, String event, in Bundle extras, in Session.Info sessionInfo); - void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts, - in CallScreeningService.ParcelableCallResponse callScreeningResponse, - boolean isResponseFromSystemDialer, in Session.Info sessionInfo); + void onCallFilteringCompleted(String callId, + in Connection.CallFilteringCompletionInfo completionInfo, + in Session.Info sessionInfo); void onExtrasChanged(String callId, in Bundle extras, in Session.Info sessionInfo); diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java index c6757fba4d53..4ae11b8458cb 100644 --- a/telephony/java/android/telephony/Annotation.java +++ b/telephony/java/android/telephony/Annotation.java @@ -111,6 +111,7 @@ public class Annotation { public @interface NetworkType { } + // TODO(b/180542000): remove and replace references with @ApnSetting.ApnType @IntDef(flag = true, prefix = {"TYPE_"}, value = { ApnSetting.TYPE_DEFAULT, ApnSetting.TYPE_MMS, @@ -124,6 +125,7 @@ public class Annotation { ApnSetting.TYPE_EMERGENCY, ApnSetting.TYPE_MCX, ApnSetting.TYPE_XCAP, + // ApnSetting.TYPE_ENTERPRISE }) @Retention(RetentionPolicy.SOURCE) public @interface ApnType { diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 8c68d856d896..3d43d031868d 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -5321,7 +5321,7 @@ public class CarrierConfigManager { sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY, new String[0]); sDefaults.putStringArray(KEY_APN_PRIORITY_STRING_ARRAY, new String[] { - "default:0", "mms:2", "supl:2", "dun:2", "hipri:3", "fota:2", + "default:0", "enterprise:1", "mms:2", "supl:2", "dun:2", "hipri:3", "fota:2", "ims:2", "cbs:2", "ia:2", "emergency:2", "mcx:3", "xcap:3" }); sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]); diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java index 99a77ae5d133..c8ed82cd2a3f 100644 --- a/telephony/java/android/telephony/DataFailCause.java +++ b/telephony/java/android/telephony/DataFailCause.java @@ -917,6 +917,10 @@ public final class DataFailCause { public static final int HANDOFF_PREFERENCE_CHANGED = 0x8CB; /** Data call fail due to the slice not being allowed for the data call. */ public static final int SLICE_REJECTED = 0x8CC; + /** No matching rule available for the request, and match-all rule is not allowed for it. */ + public static final int MATCH_ALL_RULE_NOT_ALLOWED = 0x8CD; + /** If connection failed for all matching URSP rules. */ + public static final int ALL_MATCHING_RULES_FAILED = 0x8CE; //IKE error notifications message as specified in 3GPP TS 24.302 (Section 8.1.2.2). diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index 6c013df66840..4926687f6724 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -2649,6 +2649,37 @@ public final class SmsManager { */ public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl, Bundle configOverrides, PendingIntent sentIntent) { + sendMultimediaMessage(context, contentUri, locationUrl, configOverrides, sentIntent, + 0L /* messageId */); + } + + /** + * Send an MMS message + * + * Same as {@link #sendMultimediaMessage(Context context, Uri contentUri, String locationUrl, + * Bundle configOverrides, PendingIntent sentIntent)}, but adds an optional messageId. + * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this + * manager on a multi-SIM device, this operation may fail sending the MMS message because no + * suitable default subscription could be found. In this case, if {@code sentIntent} is + * non-null, then the {@link PendingIntent} will be sent with an error code + * {@code RESULT_NO_DEFAULT_SMS_APP}. See {@link #getDefault()} for more information on the + * conditions where this operation may fail. + * </p> + * + * @param context application context + * @param contentUri the content Uri from which the message pdu will be read + * @param locationUrl the optional location url where message should be sent to + * @param configOverrides the carrier-specific messaging configuration values to override for + * sending the message. + * @param sentIntent if not NULL this <code>PendingIntent</code> is + * broadcast when the message is successfully sent, or failed + * @param messageId an id that uniquely identifies the message requested to be sent. + * Used for logging and diagnostics purposes. The id may be 0. + * @throws IllegalArgumentException if contentUri is empty + */ + public void sendMultimediaMessage(@NonNull Context context, @NonNull Uri contentUri, + @Nullable String locationUrl, @Nullable Bundle configOverrides, + @Nullable PendingIntent sentIntent, long messageId) { if (contentUri == null) { throw new IllegalArgumentException("Uri contentUri null"); } @@ -2658,7 +2689,7 @@ public final class SmsManager { @Override public void onSuccess(int subId) { m.sendMultimediaMessage(subId, contentUri, locationUrl, configOverrides, - sentIntent, 0L /* messageId */); + sentIntent, messageId); } @Override @@ -2692,6 +2723,39 @@ public final class SmsManager { */ public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent) { + downloadMultimediaMessage(context, locationUrl, contentUri, configOverrides, + downloadedIntent, 0L /* messageId */); + } + + /** + * Download an MMS message from carrier by a given location URL + * + * Same as {@link #downloadMultimediaMessage(Context context, String locationUrl, + * Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent)}, + * but adds an optional messageId. + * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this + * manager on a multi-SIM device, this operation may fail downloading the MMS message because no + * suitable default subscription could be found. In this case, if {@code downloadedIntent} is + * non-null, then the {@link PendingIntent} will be sent with an error code + * {@code RESULT_NO_DEFAULT_SMS_APP}. See {@link #getDefault()} for more information on the + * conditions where this operation may fail. + * </p> + * + * @param context application context + * @param locationUrl the location URL of the MMS message to be downloaded, usually obtained + * from the MMS WAP push notification + * @param contentUri the content uri to which the downloaded pdu will be written + * @param configOverrides the carrier-specific messaging configuration values to override for + * downloading the message. + * @param downloadedIntent if not NULL this <code>PendingIntent</code> is + * broadcast when the message is downloaded, or the download is failed + * @param messageId an id that uniquely identifies the message requested to be downloaded. + * Used for logging and diagnostics purposes. The id may be 0. + * @throws IllegalArgumentException if locationUrl or contentUri is empty + */ + public void downloadMultimediaMessage(@NonNull Context context, @NonNull String locationUrl, + @NonNull Uri contentUri, @Nullable Bundle configOverrides, + @Nullable PendingIntent downloadedIntent, long messageId) { if (TextUtils.isEmpty(locationUrl)) { throw new IllegalArgumentException("Empty MMS location URL"); } @@ -2704,7 +2768,7 @@ public final class SmsManager { @Override public void onSuccess(int subId) { m.downloadMultimediaMessage(subId, locationUrl, contentUri, configOverrides, - downloadedIntent, 0L /* messageId */); + downloadedIntent, messageId); } @Override diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index d58fa912dce2..b503733f8de9 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -28,8 +28,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.provider.Telephony; import android.provider.Telephony.Carriers; -import android.telephony.Annotation; -import android.telephony.Annotation.ApnType; import android.telephony.Annotation.NetworkType; import android.telephony.ServiceState; import android.telephony.TelephonyManager; @@ -116,6 +114,31 @@ public class ApnSetting implements Parcelable { public static final int TYPE_MCX = ApnTypes.MCX; /** APN type for XCAP. */ public static final int TYPE_XCAP = ApnTypes.XCAP; + /** + * APN type for ENTERPRISE. + * @hide + */ + public static final int TYPE_ENTERPRISE = TYPE_XCAP << 1; + + /** @hide */ + @IntDef(flag = true, prefix = {"TYPE_"}, value = { + TYPE_DEFAULT, + TYPE_MMS, + TYPE_SUPL, + TYPE_DUN, + TYPE_HIPRI, + TYPE_FOTA, + TYPE_IMS, + TYPE_CBS, + TYPE_IA, + TYPE_EMERGENCY, + TYPE_MCX, + TYPE_XCAP, + TYPE_ENTERPRISE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ApnType { + } // Possible values for authentication types. /** No authentication type. */ @@ -151,6 +174,7 @@ public class ApnSetting implements Parcelable { TYPE_MMS_STRING, TYPE_SUPL_STRING, TYPE_XCAP_STRING, + TYPE_ENTERPRISE_STRING, }, prefix = "TYPE_", suffix = "_STRING") @Retention(RetentionPolicy.SOURCE) public @interface ApnTypeString {} @@ -291,6 +315,12 @@ public class ApnSetting implements Parcelable { @SystemApi public static final String TYPE_XCAP_STRING = "xcap"; + /** + * APN type for ENTERPRISE traffic. + * @hide + */ + public static final String TYPE_ENTERPRISE_STRING = "enterprise"; + /** @hide */ @IntDef(prefix = { "AUTH_TYPE_" }, value = { @@ -370,6 +400,7 @@ public class ApnSetting implements Parcelable { APN_TYPE_STRING_MAP.put(TYPE_EMERGENCY_STRING, TYPE_EMERGENCY); APN_TYPE_STRING_MAP.put(TYPE_MCX_STRING, TYPE_MCX); APN_TYPE_STRING_MAP.put(TYPE_XCAP_STRING, TYPE_XCAP); + APN_TYPE_STRING_MAP.put(TYPE_ENTERPRISE_STRING, TYPE_ENTERPRISE); APN_TYPE_INT_MAP = new ArrayMap<>(); APN_TYPE_INT_MAP.put(TYPE_DEFAULT, TYPE_DEFAULT_STRING); @@ -384,6 +415,7 @@ public class ApnSetting implements Parcelable { APN_TYPE_INT_MAP.put(TYPE_EMERGENCY, TYPE_EMERGENCY_STRING); APN_TYPE_INT_MAP.put(TYPE_MCX, TYPE_MCX_STRING); APN_TYPE_INT_MAP.put(TYPE_XCAP, TYPE_XCAP_STRING); + APN_TYPE_INT_MAP.put(TYPE_ENTERPRISE, TYPE_ENTERPRISE_STRING); PROTOCOL_STRING_MAP = new ArrayMap<>(); PROTOCOL_STRING_MAP.put("IP", PROTOCOL_IP); @@ -1490,7 +1522,7 @@ public class ApnSetting implements Parcelable { * @hide */ @SystemApi - public static @NonNull @ApnTypeString String getApnTypeString(@Annotation.ApnType int apnType) { + public static @NonNull @ApnTypeString String getApnTypeString(@ApnType int apnType) { if (apnType == TYPE_ALL) { return "*"; } @@ -1503,7 +1535,7 @@ public class ApnSetting implements Parcelable { * when provided with an invalid int for compatibility purposes. * @hide */ - public static @NonNull String getApnTypeStringInternal(@Annotation.ApnType int apnType) { + public static @NonNull String getApnTypeStringInternal(@ApnType int apnType) { String result = getApnTypeString(apnType); return TextUtils.isEmpty(result) ? "Unknown" : result; } @@ -1517,7 +1549,7 @@ public class ApnSetting implements Parcelable { * @hide */ @SystemApi - public static @Annotation.ApnType int getApnTypeInt(@NonNull @ApnTypeString String apnType) { + public static @ApnType int getApnTypeInt(@NonNull @ApnTypeString String apnType) { return APN_TYPE_STRING_MAP.getOrDefault(apnType.toLowerCase(), 0); } @@ -2162,7 +2194,7 @@ public class ApnSetting implements Parcelable { public ApnSetting build() { if ((mApnTypeBitmask & (TYPE_DEFAULT | TYPE_MMS | TYPE_SUPL | TYPE_DUN | TYPE_HIPRI | TYPE_FOTA | TYPE_IMS | TYPE_CBS | TYPE_IA | TYPE_EMERGENCY | TYPE_MCX - | TYPE_XCAP)) == 0 + | TYPE_XCAP | TYPE_ENTERPRISE)) == 0 || TextUtils.isEmpty(mApnName) || TextUtils.isEmpty(mEntryName)) { return null; } diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java index bd4bf0740ca1..a76422977cb6 100644 --- a/telephony/java/android/telephony/data/DataCallResponse.java +++ b/telephony/java/android/telephony/data/DataCallResponse.java @@ -138,6 +138,7 @@ public final class DataCallResponse implements Parcelable { private final Qos mDefaultQos; private final List<QosBearerSession> mQosBearerSessions; private final SliceInfo mSliceInfo; + private final List<TrafficDescriptor> mTrafficDescriptors; /** * @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error. @@ -189,6 +190,7 @@ public final class DataCallResponse implements Parcelable { mDefaultQos = null; mQosBearerSessions = new ArrayList<>(); mSliceInfo = null; + mTrafficDescriptors = new ArrayList<>(); } private DataCallResponse(@DataFailureCause int cause, long suggestedRetryTime, int id, @@ -198,7 +200,7 @@ public final class DataCallResponse implements Parcelable { @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6, @HandoverFailureMode int handoverFailureMode, int pduSessionId, @Nullable Qos defaultQos, @Nullable List<QosBearerSession> qosBearerSessions, - @Nullable SliceInfo sliceInfo) { + @Nullable SliceInfo sliceInfo, @Nullable List<TrafficDescriptor> trafficDescriptors) { mCause = cause; mSuggestedRetryTime = suggestedRetryTime; mId = id; @@ -219,8 +221,11 @@ public final class DataCallResponse implements Parcelable { mHandoverFailureMode = handoverFailureMode; mPduSessionId = pduSessionId; mDefaultQos = defaultQos; - mQosBearerSessions = qosBearerSessions; + mQosBearerSessions = (qosBearerSessions == null) + ? new ArrayList<>() : new ArrayList<>(qosBearerSessions); mSliceInfo = sliceInfo; + mTrafficDescriptors = (trafficDescriptors == null) + ? new ArrayList<>() : new ArrayList<>(trafficDescriptors); } /** @hide */ @@ -249,6 +254,8 @@ public final class DataCallResponse implements Parcelable { mQosBearerSessions = new ArrayList<>(); source.readList(mQosBearerSessions, QosBearerSession.class.getClassLoader()); mSliceInfo = source.readParcelable(SliceInfo.class.getClassLoader()); + mTrafficDescriptors = new ArrayList<>(); + source.readList(mTrafficDescriptors, TrafficDescriptor.class.getClassLoader()); } /** @@ -381,7 +388,6 @@ public final class DataCallResponse implements Parcelable { * * @hide */ - @Nullable public Qos getDefaultQos() { return mDefaultQos; @@ -406,6 +412,14 @@ public final class DataCallResponse implements Parcelable { return mSliceInfo; } + /** + * @return The traffic descriptors related to this data connection. + */ + @NonNull + public List<TrafficDescriptor> getTrafficDescriptors() { + return mTrafficDescriptors; + } + @NonNull @Override public String toString() { @@ -429,6 +443,7 @@ public final class DataCallResponse implements Parcelable { .append(" defaultQos=").append(mDefaultQos) .append(" qosBearerSessions=").append(mQosBearerSessions) .append(" sliceInfo=").append(mSliceInfo) + .append(" trafficDescriptors=").append(mTrafficDescriptors) .append("}"); return sb.toString(); } @@ -443,15 +458,22 @@ public final class DataCallResponse implements Parcelable { DataCallResponse other = (DataCallResponse) o; - final boolean isQosSame = (mDefaultQos == null || other.mDefaultQos == null) ? - mDefaultQos == other.mDefaultQos : - mDefaultQos.equals(other.mDefaultQos); + final boolean isQosSame = (mDefaultQos == null || other.mDefaultQos == null) + ? mDefaultQos == other.mDefaultQos + : mDefaultQos.equals(other.mDefaultQos); - final boolean isQosBearerSessionsSame = (mQosBearerSessions == null || mQosBearerSessions == null) ? - mQosBearerSessions == other.mQosBearerSessions : - mQosBearerSessions.size() == other.mQosBearerSessions.size() + final boolean isQosBearerSessionsSame = + (mQosBearerSessions == null || other.mQosBearerSessions == null) + ? mQosBearerSessions == other.mQosBearerSessions + : mQosBearerSessions.size() == other.mQosBearerSessions.size() && mQosBearerSessions.containsAll(other.mQosBearerSessions); + final boolean isTrafficDescriptorsSame = + (mTrafficDescriptors == null || other.mTrafficDescriptors == null) + ? mTrafficDescriptors == other.mTrafficDescriptors + : mTrafficDescriptors.size() == other.mTrafficDescriptors.size() + && mTrafficDescriptors.containsAll(other.mTrafficDescriptors); + return mCause == other.mCause && mSuggestedRetryTime == other.mSuggestedRetryTime && mId == other.mId @@ -473,7 +495,8 @@ public final class DataCallResponse implements Parcelable { && mPduSessionId == other.mPduSessionId && isQosSame && isQosBearerSessionsSame - && Objects.equals(mSliceInfo, other.mSliceInfo); + && Objects.equals(mSliceInfo, other.mSliceInfo) + && isTrafficDescriptorsSame; } @Override @@ -481,7 +504,7 @@ public final class DataCallResponse implements Parcelable { return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, mDefaultQos, - mQosBearerSessions, mSliceInfo); + mQosBearerSessions, mSliceInfo, mTrafficDescriptors); } @Override @@ -517,6 +540,7 @@ public final class DataCallResponse implements Parcelable { } dest.writeList(mQosBearerSessions); dest.writeParcelable(mSliceInfo, flags); + dest.writeList(mTrafficDescriptors); } public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR = @@ -602,6 +626,8 @@ public final class DataCallResponse implements Parcelable { private SliceInfo mSliceInfo; + private List<TrafficDescriptor> mTrafficDescriptors = new ArrayList<>(); + /** * Default constructor for Builder. */ @@ -841,6 +867,24 @@ public final class DataCallResponse implements Parcelable { } /** + * The traffic descriptors for this data connection, as defined in 3GPP TS 24.526 + * Section 5.2. They are used for URSP traffic matching as described in 3GPP TS 24.526 + * Section 4.2.2. They includes an optional DNN, which, if present, must be used for traffic + * matching; it does not specify the end point to be used for the data call. The end point + * is specified by {@link DataProfile}, which must be used as the end point if one is not + * specified through URSP rules. + * + * @param trafficDescriptors the traffic descriptors for the data call. + * + * @return The same instance of the builder. + */ + public @NonNull Builder setTrafficDescriptors( + @NonNull List<TrafficDescriptor> trafficDescriptors) { + mTrafficDescriptors = trafficDescriptors; + return this; + } + + /** * Build the DataCallResponse. * * @return the DataCallResponse object. @@ -849,7 +893,7 @@ public final class DataCallResponse implements Parcelable { return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, - mDefaultQos, mQosBearerSessions, mSliceInfo); + mDefaultQos, mQosBearerSessions, mSliceInfo, mTrafficDescriptors); } } } diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java index 484c318c1ac0..f5f29c65b7cd 100644 --- a/telephony/java/android/telephony/data/DataService.java +++ b/telephony/java/android/telephony/data/DataService.java @@ -200,6 +200,17 @@ public abstract class DataService extends Service { * handover is occurring from EPDG to 5G. If the slice passed is rejected, then * {@link DataCallResponse#getCause()} is * {@link android.telephony.DataFailCause#SLICE_REJECTED}. + * @param trafficDescriptor {@link TrafficDescriptor} for which data connection needs to be + * established. It is used for URSP traffic matching as described in 3GPP TS 24.526 + * Section 4.2.2. It includes an optional DNN which, if present, must be used for + * traffic matching; it does not specify the end point to be used for the data call. + * @param matchAllRuleAllowed Indicates if using default match-all URSP rule for this + * request is allowed. If false, this request must not use the match-all URSP rule + * and if a non-match-all rule is not found (or if URSP rules are not available) then + * {@link DataCallResponse#getCause()} is + * {@link android.telephony.DataFailCause#MATCH_ALL_RULE_NOT_ALLOWED}. This is needed + * as some requests need to have a hard failure if the intention cannot be met, + * for example, a zero-rating slice. * @param callback The result callback for this request. */ public void setupDataCall(int accessNetworkType, @NonNull DataProfile dataProfile, @@ -207,6 +218,7 @@ public abstract class DataService extends Service { @SetupDataReason int reason, @Nullable LinkProperties linkProperties, @IntRange(from = 0, to = 15) int pduSessionId, @Nullable SliceInfo sliceInfo, + @Nullable TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed, @NonNull DataServiceCallback callback) { /* Call the old version since the new version isn't supported */ setupDataCall(accessNetworkType, dataProfile, isRoaming, allowRoaming, reason, @@ -403,10 +415,13 @@ public abstract class DataService extends Service { public final LinkProperties linkProperties; public final int pduSessionId; public final SliceInfo sliceInfo; + public final TrafficDescriptor trafficDescriptor; + public final boolean matchAllRuleAllowed; public final IDataServiceCallback callback; SetupDataCallRequest(int accessNetworkType, DataProfile dataProfile, boolean isRoaming, - boolean allowRoaming, int reason, LinkProperties linkProperties, - int pduSessionId, SliceInfo sliceInfo, IDataServiceCallback callback) { + boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId, + SliceInfo sliceInfo, TrafficDescriptor trafficDescriptor, + boolean matchAllRuleAllowed, IDataServiceCallback callback) { this.accessNetworkType = accessNetworkType; this.dataProfile = dataProfile; this.isRoaming = isRoaming; @@ -415,6 +430,8 @@ public abstract class DataService extends Service { this.reason = reason; this.pduSessionId = pduSessionId; this.sliceInfo = sliceInfo; + this.trafficDescriptor = trafficDescriptor; + this.matchAllRuleAllowed = matchAllRuleAllowed; this.callback = callback; } } @@ -525,7 +542,8 @@ public abstract class DataService extends Service { setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming, setupDataCallRequest.allowRoaming, setupDataCallRequest.reason, setupDataCallRequest.linkProperties, setupDataCallRequest.pduSessionId, - setupDataCallRequest.sliceInfo, + setupDataCallRequest.sliceInfo, setupDataCallRequest.trafficDescriptor, + setupDataCallRequest.matchAllRuleAllowed, (setupDataCallRequest.callback != null) ? new DataServiceCallback(setupDataCallRequest.callback) : null); @@ -690,11 +708,12 @@ public abstract class DataService extends Service { public void setupDataCall(int slotIndex, int accessNetworkType, DataProfile dataProfile, boolean isRoaming, boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId, SliceInfo sliceInfo, + TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed, IDataServiceCallback callback) { mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, slotIndex, 0, new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming, allowRoaming, reason, linkProperties, pduSessionId, sliceInfo, - callback)) + trafficDescriptor, matchAllRuleAllowed, callback)) .sendToTarget(); } diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl index e0b9a1a9bb5a..81f5fd3b69a9 100644 --- a/telephony/java/android/telephony/data/IDataService.aidl +++ b/telephony/java/android/telephony/data/IDataService.aidl @@ -20,6 +20,7 @@ import android.net.LinkProperties; import android.telephony.data.DataProfile; import android.telephony.data.IDataServiceCallback; import android.telephony.data.SliceInfo; +import android.telephony.data.TrafficDescriptor; /** * {@hide} @@ -30,7 +31,9 @@ oneway interface IDataService void removeDataServiceProvider(int slotId); void setupDataCall(int slotId, int accessNetwork, in DataProfile dataProfile, boolean isRoaming, boolean allowRoaming, int reason, in LinkProperties linkProperties, - int pduSessionId, in SliceInfo sliceInfo, IDataServiceCallback callback); + int pduSessionId, in SliceInfo sliceInfo, + in TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed, + IDataServiceCallback callback); void deactivateDataCall(int slotId, int cid, int reason, IDataServiceCallback callback); void setInitialAttachApn(int slotId, in DataProfile dataProfile, boolean isRoaming, IDataServiceCallback callback); diff --git a/telephony/java/android/telephony/data/TrafficDescriptor.aidl b/telephony/java/android/telephony/data/TrafficDescriptor.aidl new file mode 100644 index 000000000000..a9c7604a91b6 --- /dev/null +++ b/telephony/java/android/telephony/data/TrafficDescriptor.aidl @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** @hide */ +package android.telephony.data; + +parcelable TrafficDescriptor; diff --git a/telephony/java/android/telephony/data/TrafficDescriptor.java b/telephony/java/android/telephony/data/TrafficDescriptor.java new file mode 100644 index 000000000000..480379d641b6 --- /dev/null +++ b/telephony/java/android/telephony/data/TrafficDescriptor.java @@ -0,0 +1,111 @@ +/* + * 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.telephony.data; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * A traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2. It is used for URSP traffic + * matching as described in 3GPP TS 24.526 Section 4.2.2. It includes an optional DNN, which, + * if present, must be used for traffic matching; it does not specify the end point to be used for + * the data call. + * @hide + */ +@SystemApi +public final class TrafficDescriptor implements Parcelable { + private final String mDnn; + private final String mOsAppId; + + private TrafficDescriptor(@NonNull Parcel in) { + mDnn = in.readString(); + mOsAppId = in.readString(); + } + + /** + * Create a traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2 + * @param dnn optional DNN, which must be used for traffic matching, if present + * @param osAppId OsId + osAppId of the traffic descriptor + */ + public TrafficDescriptor(@Nullable String dnn, @Nullable String osAppId) { + mDnn = dnn; + mOsAppId = osAppId; + } + + /** + * DNN stands for Data Network Name and represents an APN as defined in 3GPP TS 23.003. + * @return the DNN of this traffic descriptor. + */ + public @Nullable String getDnn() { + return mDnn; + } + + /** + * OsAppId represents the OsId + OsAppId as defined in 3GPP TS 24.526 Section 5.2. + * @return the OS App ID of this traffic descriptor. + */ + public @Nullable String getOsAppId() { + return mOsAppId; + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull @Override + public String toString() { + return "TrafficDescriptor={mDnn=" + mDnn + ", mOsAppId=" + mOsAppId + "}"; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(mDnn); + dest.writeString(mOsAppId); + } + + public static final @NonNull Parcelable.Creator<TrafficDescriptor> CREATOR = + new Parcelable.Creator<TrafficDescriptor>() { + @Override + public @NonNull TrafficDescriptor createFromParcel(@NonNull Parcel source) { + return new TrafficDescriptor(source); + } + + @Override + public @NonNull TrafficDescriptor[] newArray(int size) { + return new TrafficDescriptor[size]; + } + }; + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TrafficDescriptor that = (TrafficDescriptor) o; + return Objects.equals(mDnn, that.mDnn) && Objects.equals(mOsAppId, that.mOsAppId); + } + + @Override + public int hashCode() { + return Objects.hash(mDnn, mOsAppId); + } +} diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 5c255243f52b..0048d53f9172 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -2055,6 +2055,11 @@ interface ITelephony { int setImsProvisioningString(int subId, int key, String value); /** + * Start emergency callback mode for testing. + */ + void startEmergencyCallbackMode(); + + /** * Update Emergency Number List for Test Mode. */ void updateEmergencyNumberListTestMode(int action, in EmergencyNumber num); diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java index 151187c5071f..3a99f0e010c6 100644 --- a/telephony/java/com/android/internal/telephony/PhoneConstants.java +++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java @@ -126,6 +126,7 @@ public class PhoneConstants { * connections.<br/> * APN_TYPE_ALL is a special type to indicate that this APN entry can * service all data connections. + * TODO: remove these and use the reference to ApnSetting.TYPE_XXX_STRING instead */ public static final String APN_TYPE_ALL = ApnSetting.TYPE_ALL_STRING; /** APN type for default data traffic */ @@ -153,20 +154,8 @@ public class PhoneConstants { public static final String APN_TYPE_MCX = ApnSetting.TYPE_MCX_STRING; /** APN type for XCAP */ public static final String APN_TYPE_XCAP = ApnSetting.TYPE_XCAP_STRING; - /** Array of all APN types */ - public static final String[] APN_TYPES = {APN_TYPE_DEFAULT, - APN_TYPE_MMS, - APN_TYPE_SUPL, - APN_TYPE_DUN, - APN_TYPE_HIPRI, - APN_TYPE_FOTA, - APN_TYPE_IMS, - APN_TYPE_CBS, - APN_TYPE_IA, - APN_TYPE_EMERGENCY, - APN_TYPE_MCX, - APN_TYPE_XCAP, - }; + // /** APN type for enterprise */ + // public static final String APN_TYPE_ENTERPRISE = ApnSetting.TYPE_ENTERPRISE_STRING; public static final int RIL_CARD_MAX_APPS = 8; diff --git a/test-base/Android.bp b/test-base/Android.bp index 0b7a3981a403..9bd639b63ae0 100644 --- a/test-base/Android.bp +++ b/test-base/Android.bp @@ -49,6 +49,12 @@ java_sdk_library { compile_dex: true, default_to_stubs: true, + + // Additional hiddenapi annotations are provided in a separate module. + // TODO(b/180295980) - investigate whether this can be removed + hiddenapi_additional_annotations: [ + "android.test.base-hiddenapi-annotations", + ], } // Build the android.test.base_static library @@ -91,8 +97,9 @@ java_library_static { // =============================================== // This contains the android.test classes from android.test.base plus // the com.android.internal.util.Predicate[s] classes. This is only -// intended for inclusion in android.test.legacy and must not be used -// elsewhere. +// intended for inclusion in android.test.legacy and in +// android.test.base-hiddenapi-annotations to avoid a dependency cycle and must +// not be used elsewhere. java_library_static { name: "android.test.base-minus-junit", diff --git a/test-base/hiddenapi/Android.bp b/test-base/hiddenapi/Android.bp index d4f52d0fc6cd..1466590ef311 100644 --- a/test-base/hiddenapi/Android.bp +++ b/test-base/hiddenapi/Android.bp @@ -14,11 +14,6 @@ // limitations under the License. // -// Provided solely to contribute information about which hidden parts of the android.test.base -// library are used by apps. The source files are stubs of the actual files in ../src which use the -// UnsupportedAppUsage annotation to tag those methods that are accessible via the hiddenapi. -// Relies on the convention that modules with name <x>-hiddenapi provide hiddenapi information for -// module <x> that is on the bootclasspath. package { // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import @@ -28,14 +23,20 @@ package { default_applicable_licenses: ["frameworks_base_license"], } +// Provided solely to contribute information about which hidden parts of the android.test.base +// library are used by apps. The source files are stubs of the actual files in ../src which use the +// UnsupportedAppUsage annotation to tag those methods that are accessible via the hiddenapi. java_library { - name: "android.test.base-hiddenapi", + name: "android.test.base-hiddenapi-annotations", compile_dex: true, srcs: ["src/**/*.java"], libs: [ - "android.test.base", + // Use this instead of `android.test.base` to avoid a dependency cycle + // as `android.test.base` depends on this. + "android.test.base-minus-junit", + "junit", "unsupportedappusage", ], } diff --git a/tests/BatteryStatsPerfTest/Android.bp b/tests/BatteryStatsPerfTest/Android.bp index 58ccec705419..5233a5b8654e 100644 --- a/tests/BatteryStatsPerfTest/Android.bp +++ b/tests/BatteryStatsPerfTest/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test { name: "BatteryStatsPerfTests", srcs: ["src/**/*.java"], 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 c5447c1ccf71..7d29cdd2b5c5 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt @@ -17,382 +17,91 @@ 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.EventLogAssertionBuilderLegacy -import com.android.server.wm.flicker.dsl.LayersAssertionBuilder -import com.android.server.wm.flicker.dsl.LayersAssertionBuilderLegacy -import com.android.server.wm.flicker.dsl.WmAssertionBuilder -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 -import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.STATUS_BAR_LAYER_NAME import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.STATUS_BAR_WINDOW_NAME const val APP_PAIR_SPLIT_DIVIDER = "AppPairSplitDivider" const val DOCKED_STACK_DIVIDER = "DockedStackDivider" const val WALLPAPER_TITLE = "Wallpaper" -@JvmOverloads -fun WmAssertionBuilder.statusBarWindowIsAlwaysVisible(bugId: Int = 0) { - all("statusBarWindowIsAlwaysVisible", bugId) { +fun FlickerTestParameter.statusBarWindowIsAlwaysVisible() { + assertWm { this.showsAboveAppWindow(NAV_BAR_LAYER_NAME) } } -@JvmOverloads -fun WmAssertionBuilder.navBarWindowIsAlwaysVisible(bugId: Int = 0) { - all("navBarWindowIsAlwaysVisible", bugId) { +fun FlickerTestParameter.navBarWindowIsAlwaysVisible() { + assertWm { this.showsAboveAppWindow(NAV_BAR_LAYER_NAME) } } -fun WmAssertionBuilder.visibleWindowsShownMoreThanOneConsecutiveEntry( - ignoreWindows: List<String> = emptyList(), - bugId: Int = 0 -) { - all("visibleWindowsShownMoreThanOneConsecutiveEntry", bugId) { - this.visibleWindowsShownMoreThanOneConsecutiveEntry(ignoreWindows) - } -} - -fun WmAssertionBuilder.launcherReplacesAppWindowAsTopWindow(testApp: IAppHelper, bugId: Int = 0) { - all("launcherReplacesAppWindowAsTopWindow", bugId) { - this.showsAppWindowOnTop(testApp.getPackage()) - .then() - .showsAppWindowOnTop("Launcher") - } -} - -fun WmAssertionBuilder.wallpaperWindowBecomesVisible(bugId: Int = 0) { - all("wallpaperWindowBecomesVisible", bugId) { - this.hidesBelowAppWindow(WALLPAPER_TITLE) - .then() - .showsBelowAppWindow(WALLPAPER_TITLE) - } -} - -fun WmAssertionBuilder.wallpaperWindowBecomesInvisible(bugId: Int = 0) { - all("wallpaperWindowBecomesInvisible", bugId) { - this.showsBelowAppWindow("Wallpaper") - .then() - .hidesBelowAppWindow("Wallpaper") - } -} - -fun WmAssertionBuilder.appWindowAlwaysVisibleOnTop( - packageName: String, - bugId: Int = 0 -) { - all("appWindowAlwaysVisibleOnTop", bugId) { - this.showsAppWindowOnTop(packageName) - } -} - -fun WmAssertionBuilder.appWindowBecomesVisible(appName: String, bugId: Int = 0) { - all("appWindowBecomesVisible", bugId) { - this.hidesAppWindow(appName) - .then() - .showsAppWindow(appName) - } -} - -fun WmAssertionBuilder.appWindowBecomesInVisible(appName: String, bugId: Int = 0) { - all("appWindowBecomesInVisible", bugId) { - this.showsAppWindow(appName) - .then() - .hidesAppWindow(appName) - } -} - -@JvmOverloads -fun LayersAssertionBuilder.noUncoveredRegions( - beginRotation: Int, - endRotation: Int = beginRotation, - allStates: Boolean = true, - bugId: Int = 0 -) { - val startingBounds = WindowUtils.getDisplayBounds(beginRotation) - val endingBounds = WindowUtils.getDisplayBounds(endRotation) - if (allStates) { - all("noUncoveredRegions", bugId) { - if (startingBounds == endingBounds) { - this.coversAtLeastRegion(startingBounds) - } else { - this.coversAtLeastRegion(startingBounds) - .then() - .coversAtLeastRegion(endingBounds) - } - } - } else { - start("noUncoveredRegions_StartingPos") { - this.coversAtLeastRegion(startingBounds) - } - end("noUncoveredRegions_EndingPos") { - this.coversAtLeastRegion(endingBounds) - } - } -} - -@JvmOverloads -fun LayersAssertionBuilder.navBarLayerIsAlwaysVisible( - rotatesScreen: Boolean = false, - bugId: Int = 0 -) { - if (rotatesScreen) { - all("navBarLayerIsAlwaysVisible", bugId) { - this.showsLayer(NAV_BAR_LAYER_NAME) - .then() - .hidesLayer(NAV_BAR_LAYER_NAME) - .then() - .showsLayer(NAV_BAR_LAYER_NAME) - } - } else { - all("navBarLayerIsAlwaysVisible", bugId) { - this.showsLayer(NAV_BAR_LAYER_NAME) - } - } -} - -@JvmOverloads -fun LayersAssertionBuilder.statusBarLayerIsAlwaysVisible( - rotatesScreen: Boolean = false, - bugId: Int = 0 -) { - if (rotatesScreen) { - all("statusBarLayerIsAlwaysVisible", bugId) { - this.showsLayer(STATUS_BAR_WINDOW_NAME) - .then() - hidesLayer(STATUS_BAR_WINDOW_NAME) - .then() - .showsLayer(STATUS_BAR_WINDOW_NAME) - } - } else { - all("statusBarLayerIsAlwaysVisible", bugId) { - this.showsLayer(STATUS_BAR_WINDOW_NAME) - } - } -} - -@JvmOverloads -fun LayersAssertionBuilder.navBarLayerRotatesAndScales( - beginRotation: Int, - endRotation: Int = beginRotation, - bugId: Int = 0 -) { - val startingPos = WindowUtils.getNavigationBarPosition(beginRotation) - val endingPos = WindowUtils.getNavigationBarPosition(endRotation) - - start("navBarLayerRotatesAndScales_StartingPos", bugId) { - this.hasVisibleRegion(NAV_BAR_LAYER_NAME, startingPos) - } - end("navBarLayerRotatesAndScales_EndingPost", bugId) { - this.hasVisibleRegion(NAV_BAR_LAYER_NAME, endingPos) - } - - /*if (startingPos == endingPos) { - all("navBarLayerRotatesAndScales", enabled = false, bugId = 167747321) { - this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos) - } - }*/ -} - -@JvmOverloads -fun LayersAssertionBuilder.statusBarLayerRotatesScales( - beginRotation: Int, - endRotation: Int = beginRotation, - bugId: Int = 0 -) { - val startingPos = WindowUtils.getStatusBarPosition(beginRotation) - val endingPos = WindowUtils.getStatusBarPosition(endRotation) - - start("statusBarLayerRotatesScales_StartingPos", bugId) { - this.hasVisibleRegion(STATUS_BAR_WINDOW_NAME, startingPos) - } - end("statusBarLayerRotatesScales_EndingPos", bugId) { - this.hasVisibleRegion(STATUS_BAR_WINDOW_NAME, endingPos) - } -} - -fun LayersAssertionBuilder.visibleLayersShownMoreThanOneConsecutiveEntry( - ignoreLayers: List<String> = emptyList(), - bugId: Int = 0 -) { - all("visibleLayersShownMoreThanOneConsecutiveEntry", bugId) { - this.visibleLayersShownMoreThanOneConsecutiveEntry(ignoreLayers) - } -} - -fun LayersAssertionBuilder.appLayerReplacesWallpaperLayer(appName: String, bugId: Int = 0) { - all("appLayerReplacesWallpaperLayer", bugId) { - this.showsLayer("Wallpaper") - .then() - .replaceVisibleLayer("Wallpaper", appName) - } -} - -fun LayersAssertionBuilder.wallpaperLayerReplacesAppLayer(testApp: IAppHelper, bugId: Int = 0) { - all("appLayerReplacesWallpaperLayer", bugId) { - this.showsLayer(testApp.getPackage()) - .then() - .replaceVisibleLayer(testApp.getPackage(), WALLPAPER_TITLE) - } -} - -fun LayersAssertionBuilder.layerAlwaysVisible(packageName: String, bugId: Int = 0) { - all("layerAlwaysVisible", bugId) { - this.showsLayer(packageName) - } -} - -fun LayersAssertionBuilder.layerBecomesVisible(packageName: String, bugId: Int = 0) { - all("layerBecomesVisible", bugId) { - this.hidesLayer(packageName) - .then() - .showsLayer(packageName) - } -} - -fun LayersAssertionBuilder.layerBecomesInvisible(packageName: String, bugId: Int = 0) { - all("layerBecomesInvisible", bugId) { - this.showsLayer(packageName) - .then() - .hidesLayer(packageName) - } -} - -fun EventLogAssertionBuilder.focusChanges(vararg windows: String, bugId: Int = 0) { - all("focusChanges", bugId) { - this.focusChanges(windows) - } -} - -fun EventLogAssertionBuilder.focusDoesNotChange(bugId: Int = 0) { - all("focusDoesNotChange", bugId) { - this.focusDoesNotChange() - } -} - @JvmOverloads -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun WmAssertionBuilderLegacy.statusBarWindowIsAlwaysVisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 +fun FlickerTestParameter.visibleWindowsShownMoreThanOneConsecutiveEntry( + ignoreWindows: List<String> = emptyList() ) { - all("statusBarWindowIsAlwaysVisible", bugId, enabled) { - this.showsAboveAppWindow(STATUS_BAR_WINDOW_NAME) - } -} - -@JvmOverloads -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun WmAssertionBuilderLegacy.navBarWindowIsAlwaysVisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("navBarWindowIsAlwaysVisible", bugId, enabled) { - this.showsAboveAppWindow(NAV_BAR_WINDOW_NAME) - } -} - -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun WmAssertionBuilderLegacy.visibleWindowsShownMoreThanOneConsecutiveEntry( - ignoreWindows: List<String> = emptyList(), - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("visibleWindowsShownMoreThanOneConsecutiveEntry", bugId, enabled) { + assertWm { this.visibleWindowsShownMoreThanOneConsecutiveEntry(ignoreWindows) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun WmAssertionBuilderLegacy.launcherReplacesAppWindowAsTopWindow( - testApp: IAppHelper, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("launcherReplacesAppWindowAsTopWindow", bugId, enabled) { +fun FlickerTestParameter.launcherReplacesAppWindowAsTopWindow(testApp: IAppHelper) { + assertWm { this.showsAppWindowOnTop(testApp.getPackage()) .then() .showsAppWindowOnTop("Launcher") } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun WmAssertionBuilderLegacy.wallpaperWindowBecomesVisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("wallpaperWindowBecomesVisible", bugId, enabled) { +fun FlickerTestParameter.wallpaperWindowBecomesVisible() { + assertWm { this.hidesBelowAppWindow(WALLPAPER_TITLE) .then() .showsBelowAppWindow(WALLPAPER_TITLE) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun WmAssertionBuilderLegacy.wallpaperWindowBecomesInvisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("wallpaperWindowBecomesInvisible", bugId, enabled) { +fun FlickerTestParameter.wallpaperWindowBecomesInvisible() { + assertWm { this.showsBelowAppWindow("Wallpaper") .then() .hidesBelowAppWindow("Wallpaper") } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun WmAssertionBuilderLegacy.appWindowAlwaysVisibleOnTop( - packageName: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("appWindowAlwaysVisibleOnTop", bugId, enabled) { +fun FlickerTestParameter.appWindowAlwaysVisibleOnTop(packageName: String) { + assertWm { this.showsAppWindowOnTop(packageName) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun WmAssertionBuilderLegacy.appWindowBecomesVisible( - appName: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("appWindowBecomesVisible", bugId, enabled) { +fun FlickerTestParameter.appWindowBecomesVisible(appName: String) { + assertWm { this.hidesAppWindow(appName) .then() .showsAppWindow(appName) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun WmAssertionBuilderLegacy.appWindowBecomesInVisible( - appName: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("appWindowBecomesInVisible", bugId, enabled) { +fun FlickerTestParameter.appWindowBecomesInVisible(appName: String) { + assertWm { this.showsAppWindow(appName) .then() .hidesAppWindow(appName) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") @JvmOverloads -fun LayersAssertionBuilderLegacy.noUncoveredRegions( +fun FlickerTestParameter.noUncoveredRegions( beginRotation: Int, endRotation: Int = beginRotation, - allStates: Boolean = true, - bugId: Int = 0, - enabled: Boolean = bugId == 0 + allStates: Boolean = true ) { val startingBounds = WindowUtils.getDisplayBounds(beginRotation) val endingBounds = WindowUtils.getDisplayBounds(endRotation) if (allStates) { - all("noUncoveredRegions", bugId, enabled) { + assertLayers { if (startingBounds == endingBounds) { this.coversAtLeastRegion(startingBounds) } else { @@ -402,24 +111,19 @@ fun LayersAssertionBuilderLegacy.noUncoveredRegions( } } } else { - start("noUncoveredRegions_StartingPos") { + assertLayersStart { this.coversAtLeastRegion(startingBounds) } - end("noUncoveredRegions_EndingPos") { + assertLayersEnd { this.coversAtLeastRegion(endingBounds) } } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") @JvmOverloads -fun LayersAssertionBuilderLegacy.navBarLayerIsAlwaysVisible( - rotatesScreen: Boolean = false, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { +fun FlickerTestParameter.navBarLayerIsAlwaysVisible(rotatesScreen: Boolean = false) { if (rotatesScreen) { - all("navBarLayerIsAlwaysVisible", bugId, enabled) { + assertLayers { this.showsLayer(NAV_BAR_LAYER_NAME) .then() .hidesLayer(NAV_BAR_LAYER_NAME) @@ -427,169 +131,116 @@ fun LayersAssertionBuilderLegacy.navBarLayerIsAlwaysVisible( .showsLayer(NAV_BAR_LAYER_NAME) } } else { - all("navBarLayerIsAlwaysVisible", bugId, enabled) { + assertLayers { this.showsLayer(NAV_BAR_LAYER_NAME) } } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") @JvmOverloads -fun LayersAssertionBuilderLegacy.statusBarLayerIsAlwaysVisible( - rotatesScreen: Boolean = false, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { +fun FlickerTestParameter.statusBarLayerIsAlwaysVisible(rotatesScreen: Boolean = false) { if (rotatesScreen) { - all("statusBarLayerIsAlwaysVisible", bugId, enabled) { - this.showsLayer(STATUS_BAR_LAYER_NAME) + assertLayers { + this.showsLayer(STATUS_BAR_WINDOW_NAME) .then() - .hidesLayer(STATUS_BAR_LAYER_NAME) + hidesLayer(STATUS_BAR_WINDOW_NAME) .then() - .showsLayer(STATUS_BAR_LAYER_NAME) + .showsLayer(STATUS_BAR_WINDOW_NAME) } } else { - all("statusBarLayerIsAlwaysVisible", bugId, enabled) { - this.showsLayer(STATUS_BAR_LAYER_NAME) + assertLayers { + this.showsLayer(STATUS_BAR_WINDOW_NAME) } } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") @JvmOverloads -fun LayersAssertionBuilderLegacy.navBarLayerRotatesAndScales( +fun FlickerTestParameter.navBarLayerRotatesAndScales( beginRotation: Int, - endRotation: Int = beginRotation, - bugId: Int = 0, - enabled: Boolean = bugId == 0 + endRotation: Int = beginRotation ) { val startingPos = WindowUtils.getNavigationBarPosition(beginRotation) val endingPos = WindowUtils.getNavigationBarPosition(endRotation) - start("navBarLayerRotatesAndScales_StartingPos", bugId, enabled) { + assertLayersStart { this.hasVisibleRegion(NAV_BAR_LAYER_NAME, startingPos) } - end("navBarLayerRotatesAndScales_EndingPost", bugId, enabled) { + assertLayersEnd { this.hasVisibleRegion(NAV_BAR_LAYER_NAME, endingPos) } - - if (startingPos == endingPos) { - all("navBarLayerRotatesAndScales", enabled = false, bugId = 167747321) { - this.hasVisibleRegion(NAV_BAR_LAYER_NAME, startingPos) - } - } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") @JvmOverloads -fun LayersAssertionBuilderLegacy.statusBarLayerRotatesScales( +fun FlickerTestParameter.statusBarLayerRotatesScales( beginRotation: Int, - endRotation: Int = beginRotation, - bugId: Int = 0, - enabled: Boolean = bugId == 0 + endRotation: Int = beginRotation ) { val startingPos = WindowUtils.getStatusBarPosition(beginRotation) val endingPos = WindowUtils.getStatusBarPosition(endRotation) - start("statusBarLayerRotatesScales_StartingPos", bugId, enabled) { - this.hasVisibleRegion(STATUS_BAR_LAYER_NAME, startingPos) + assertLayersStart { + this.hasVisibleRegion(STATUS_BAR_WINDOW_NAME, startingPos) } - end("statusBarLayerRotatesScales_EndingPos", bugId, enabled) { - this.hasVisibleRegion(STATUS_BAR_LAYER_NAME, endingPos) + assertLayersEnd { + this.hasVisibleRegion(STATUS_BAR_WINDOW_NAME, endingPos) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun LayersAssertionBuilderLegacy.visibleLayersShownMoreThanOneConsecutiveEntry( - ignoreLayers: List<String> = kotlin.collections.emptyList(), - bugId: Int = 0, - enabled: Boolean = bugId == 0 +@JvmOverloads +fun FlickerTestParameter.visibleLayersShownMoreThanOneConsecutiveEntry( + ignoreLayers: List<String> = emptyList() ) { - all("visibleLayersShownMoreThanOneConsecutiveEntry", bugId, enabled) { + assertLayers { this.visibleLayersShownMoreThanOneConsecutiveEntry(ignoreLayers) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun LayersAssertionBuilderLegacy.appLayerReplacesWallpaperLayer( - appName: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("appLayerReplacesWallpaperLayer", bugId, enabled) { +fun FlickerTestParameter.appLayerReplacesWallpaperLayer(appName: String) { + assertLayers { this.showsLayer("Wallpaper") .then() .replaceVisibleLayer("Wallpaper", appName) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun LayersAssertionBuilderLegacy.wallpaperLayerReplacesAppLayer( - testApp: IAppHelper, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("appLayerReplacesWallpaperLayer", bugId, enabled) { +fun FlickerTestParameter.wallpaperLayerReplacesAppLayer(testApp: IAppHelper) { + assertLayers { this.showsLayer(testApp.getPackage()) .then() .replaceVisibleLayer(testApp.getPackage(), WALLPAPER_TITLE) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun LayersAssertionBuilderLegacy.layerAlwaysVisible( - packageName: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("layerAlwaysVisible", bugId, enabled) { +fun FlickerTestParameter.layerAlwaysVisible(packageName: String) { + assertLayers { this.showsLayer(packageName) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun LayersAssertionBuilderLegacy.layerBecomesVisible( - packageName: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("layerBecomesVisible", bugId, enabled) { +fun FlickerTestParameter.layerBecomesVisible(packageName: String) { + assertLayers { this.hidesLayer(packageName) .then() .showsLayer(packageName) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun LayersAssertionBuilderLegacy.layerBecomesInvisible( - packageName: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("layerBecomesInvisible", bugId, enabled) { +fun FlickerTestParameter.layerBecomesInvisible(packageName: String) { + assertLayers { this.showsLayer(packageName) .then() .hidesLayer(packageName) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun EventLogAssertionBuilderLegacy.focusChanges( - vararg windows: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("focusChanges", bugId, enabled) { +fun FlickerTestParameter.focusChanges(vararg windows: String) { + assertEventLog { this.focusChanges(windows) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun EventLogAssertionBuilderLegacy.focusDoesNotChange( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("focusDoesNotChange", bugId, enabled) { +fun FlickerTestParameter.focusDoesNotChange() { + assertEventLog { this.focusDoesNotChange() } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt index c507841ffb71..fbf18d45afd8 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt @@ -16,11 +16,17 @@ package com.android.server.wm.flicker.close +import android.app.Instrumentation +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunnerFactory -import com.android.server.wm.flicker.FlickerTestRunner +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.SimpleAppHelper import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible @@ -35,12 +41,12 @@ import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -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.repetitions import com.android.server.wm.flicker.startRotation +import org.junit.Assume import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -51,86 +57,119 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseAppBackButtonTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { +class CloseAppBackButtonTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = SimpleAppHelper(instrumentation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + } + eachRun { + this.setRotation(testSpec.config.startRotation) + testApp.launchViaIntent(wmHelper) + } + } + transitions { + device.pressBack() + wmHelper.waitForHomeActivityVisible() + } + teardown { + eachRun { + this.setRotation(Surface.ROTATION_0) + } + test { + testApp.exit() + } + } + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun launcherReplacesAppWindowAsTopWindow() = + testSpec.launcherReplacesAppWindowAsTopWindow(testApp) + + @Presubmit + @Test + fun wallpaperWindowBecomesVisible() = testSpec.wallpaperWindowBecomesVisible() + + @Presubmit + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, + Surface.ROTATION_0) + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun wallpaperLayerReplacesAppLayer() = testSpec.wallpaperLayerReplacesAppLayer(testApp) + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun statusBarLayerRotatesScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest(bugId = 173684672) + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + + @FlakyTest(bugId = 173684672) + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = SimpleAppHelper(instrumentation) - return FlickerTestRunnerFactory.getInstance() - .buildTest(instrumentation, repetitions = 5) { configuration -> - withTestName { buildTestTag(configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - } - eachRun { - this.setRotation(configuration.startRotation) - testApp.launchViaIntent(wmHelper) - } - } - transitions { - device.pressBack() - wmHelper.waitForHomeActivityVisible() - } - teardown { - eachRun { - this.setRotation(Surface.ROTATION_0) - } - test { - testApp.exit() - } - } - assertions { - val isRotated = configuration.startRotation.isRotated() - - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - launcherReplacesAppWindowAsTopWindow(testApp) - wallpaperWindowBecomesVisible() - } - - layersTrace { - noUncoveredRegions(configuration.startRotation, - Surface.ROTATION_0) - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - wallpaperLayerReplacesAppLayer(testApp) - - if (!isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0) - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) - } - } - } - - flaky { - windowManagerTrace { - visibleWindowsShownMoreThanOneConsecutiveEntry(bugId = 173684672) - } - - layersTrace { - visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 173684672) - - if (isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0) - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) - } - } - } - } - } + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(repetitions = 5) } } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt index d1c3efe35c54..08d2b7c206bf 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt @@ -16,12 +16,17 @@ package com.android.server.wm.flicker.close +import android.app.Instrumentation import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunnerFactory -import com.android.server.wm.flicker.FlickerTestRunner +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.SimpleAppHelper import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible @@ -36,12 +41,12 @@ import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -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.repetitions import com.android.server.wm.flicker.startRotation +import org.junit.Assume import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -50,88 +55,121 @@ import org.junit.runners.Parameterized * Test app closes by pressing home button. * To run this test: `atest FlickerTests:CloseAppHomeButtonTest` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseAppHomeButtonTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { +class CloseAppHomeButtonTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = SimpleAppHelper(instrumentation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + } + eachRun { + testApp.launchViaIntent(wmHelper) + this.setRotation(testSpec.config.startRotation) + } + } + transitions { + device.pressHome() + wmHelper.waitForHomeActivityVisible() + } + teardown { + eachRun { + this.setRotation(Surface.ROTATION_0) + } + test { + testApp.exit() + } + } + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun launcherReplacesAppWindowAsTopWindow() = + testSpec.launcherReplacesAppWindowAsTopWindow(testApp) + + @Presubmit + @Test + fun wallpaperWindowBecomesVisible() = testSpec.wallpaperWindowBecomesVisible() + + @Presubmit + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions( + testSpec.config.startRotation, Surface.ROTATION_0) + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun wallpaperLayerReplacesAppLayer() = testSpec.wallpaperLayerReplacesAppLayer(testApp) + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun statusBarLayerRotatesScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest(bugId = 173689015) + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + + @FlakyTest(bugId = 173689015) + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = SimpleAppHelper(instrumentation) - return FlickerTestRunnerFactory.getInstance() - .buildTest(instrumentation, repetitions = 5) { configuration -> - withTestName { buildTestTag(configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - } - eachRun { - testApp.launchViaIntent(wmHelper) - this.setRotation(configuration.startRotation) - } - } - transitions { - device.pressHome() - wmHelper.waitForHomeActivityVisible() - } - teardown { - eachRun { - this.setRotation(Surface.ROTATION_0) - } - test { - testApp.exit() - } - } - assertions { - val isRotated = configuration.startRotation.isRotated() - - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - launcherReplacesAppWindowAsTopWindow(testApp) - wallpaperWindowBecomesVisible() - } - - layersTrace { - noUncoveredRegions(configuration.startRotation, - Surface.ROTATION_0) - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - wallpaperLayerReplacesAppLayer(testApp) - - if (!isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0) - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) - } - } - } - - flaky { - windowManagerTrace { - visibleWindowsShownMoreThanOneConsecutiveEntry(bugId = 173689015) - } - layersTrace { - visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 173689015) - - if (isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0) - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) - } - } - } - } - } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(repetitions = 5) } } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt index 323236ed9962..f7e749311442 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt @@ -16,13 +16,9 @@ package com.android.server.wm.flicker.helpers -import android.os.Bundle import android.os.RemoteException -import android.platform.helpers.IAppHelper import android.view.Surface import com.android.server.wm.flicker.Flicker -import com.android.server.wm.flicker.endRotation -import com.android.server.wm.flicker.startRotation /** * Changes the device [rotation] and wait for the rotation animation to complete @@ -47,128 +43,4 @@ fun Flicker.setRotation(rotation: Int) { } catch (e: RemoteException) { throw RuntimeException(e) } -} - -/** - * Build a test tag for the test - * @param testName Name of the transition(s) being tested - * @param app App being launcher - * @param beginRotation Initial screen rotation - * @param endRotation End screen rotation (if any, otherwise use same as initial) - * - * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION> -</END_ROTATION></BEGIN_ROTATION></APP></NAME> */ -fun buildTestTag( - testName: String, - app: IAppHelper, - beginRotation: Int, - endRotation: Int -): String { - return buildTestTag( - testName, app.launcherName, beginRotation, endRotation, app2 = null, extraInfo = "") -} - -/** - * Build a test tag for the test - * @param testName Name of the transition(s) being tested - * @param configuration Configuration for the test - * @param extraInfo Additional information to append to the tag - * - * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION> -</END_ROTATION></BEGIN_ROTATION></APP></NAME> */ -@JvmOverloads -fun buildTestTag( - testName: String, - configuration: Bundle, - extraInfo: String = "" -): String { - return buildTestTag(testName, - app = null, - beginRotation = configuration.startRotation, - endRotation = configuration.endRotation, - app2 = null, - extraInfo = extraInfo) -} - -/** - * Build a test tag for the test - * @param configuration Configuration for the test - * @param extraInfo Additional information to append to the tag - * - * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION> -</END_ROTATION></BEGIN_ROTATION></APP></NAME> */ -@JvmOverloads -fun buildTestTag( - configuration: Bundle, - extraInfo: String = "" -): String { - return buildTestTag(testName = null, - app = null, - beginRotation = configuration.startRotation, - endRotation = configuration.endRotation, - app2 = null, - extraInfo = extraInfo) -} - -/** - * Build a test tag for the test - * @param testName Name of the transition(s) being tested - * @param app App being launcher - * @param configuration Configuration for the test - * @param extraInfo Additional information to append to the tag - * - * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION> -</END_ROTATION></BEGIN_ROTATION></APP></NAME> */ -@JvmOverloads -fun buildTestTag( - testName: String, - app: IAppHelper?, - configuration: Bundle, - extraInfo: String = "" -): String { - return buildTestTag(testName, app?.launcherName ?: "", configuration.startRotation, - configuration.endRotation, app2 = null, extraInfo = extraInfo) -} - -/** - * Build a test tag for the test - * @param testName Name of the transition(s) being tested - * @param app App being launcher - * @param app2 Second app being launched (if any) - * @param beginRotation Initial screen rotation - * @param endRotation End screen rotation (if any, otherwise use same as initial) - * @param extraInfo Additional information to append to the tag - * - * @return test tag with pattern <NAME>__<APP></APP>(S)>__<ROTATION></ROTATION>(S)>[__<EXTRA>] -</EXTRA></NAME> */ -fun buildTestTag( - testName: String?, - app: String?, - beginRotation: Int, - endRotation: Int, - app2: String?, - extraInfo: String -): String { - var testTag = "" - if (testName != null) { - testTag += testName - } - if (app != null) { - testTag += "__$app" - } - if (app2 != null) { - testTag += "-$app2" - } - testTag += "__${Surface.rotationToString(beginRotation)}" - if (endRotation != beginRotation) { - testTag += "-${Surface.rotationToString(endRotation)}" - } - if (extraInfo.isNotEmpty()) { - testTag += "__$extraInfo" - } - - if (testTag.startsWith("__")) { - testTag = testTag.drop(2) - } - return testTag -} +}
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt index c7736f825e27..47eaddfa1f3a 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt @@ -16,14 +16,19 @@ package com.android.server.wm.flicker.ime +import android.app.Instrumentation +import android.platform.test.annotations.Postsubmit +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper -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.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry @@ -37,7 +42,9 @@ import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import org.junit.Assume import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -48,79 +55,116 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseImeAutoOpenWindowToAppTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { +class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + } + eachRun { + testApp.launchViaIntent(wmHelper) + testApp.openIME(device, wmHelper) + this.setRotation(testSpec.config.startRotation) + } + } + teardown { + test { + testApp.exit() + wmHelper.waitForAppTransitionIdle() + this.setRotation(Surface.ROTATION_0) + } + } + transitions { + testApp.closeIME(device, wmHelper) + } + } + } + + @Postsubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + + @Postsubmit + @Test + fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp) + + @Postsubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Postsubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Postsubmit + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation) + + @Postsubmit + @Test + fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible() + + @Postsubmit + @Test + fun imeAppLayerIsAlwaysVisible() = testSpec.imeAppLayerIsAlwaysVisible(testApp) + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation) + } + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation) + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation) + } + + @FlakyTest + @Test + fun statusBarLayerRotatesScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation) + } + + @FlakyTest + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - return FlickerTestRunnerFactory.getInstance() - .buildTest(instrumentation, repetitions = 5) { configuration -> - val testApp = ImeAppAutoFocusHelper(instrumentation, - configuration.startRotation) - withTestName { buildTestTag(configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - } - eachRun { - testApp.launchViaIntent(wmHelper) - testApp.openIME(device, wmHelper) - this.setRotation(configuration.startRotation) - } - } - teardown { - test { - testApp.exit() - wmHelper.waitForAppTransitionIdle() - this.setRotation(Surface.ROTATION_0) - } - } - transitions { - testApp.closeIME(device, wmHelper) - } - assertions { - val isRotated = configuration.startRotation.isRotated() - - postsubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(IME_WINDOW_TITLE)) - imeAppWindowIsAlwaysVisible(testApp) - } - - layersTrace { - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - noUncoveredRegions(configuration.startRotation) - imeLayerBecomesInvisible() - imeAppLayerIsAlwaysVisible(testApp) - if (!isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation) - statusBarLayerRotatesScales(configuration.startRotation) - } - } - } - - flaky { - layersTrace { - visibleLayersShownMoreThanOneConsecutiveEntry() - - if (isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation) - statusBarLayerRotatesScales(configuration.startRotation) - } - } - } - } - } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(repetitions = 5) } } -}
\ No newline at end of file +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt index aa24456c652f..38a88d372da4 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt @@ -16,14 +16,18 @@ package com.android.server.wm.flicker.ime +import android.app.Instrumentation +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper -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.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry @@ -37,7 +41,9 @@ import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import org.junit.Assume import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -48,93 +54,151 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseImeAutoOpenWindowToHomeTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { +class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + } + eachRun { + testApp.launchViaIntent(wmHelper) + testApp.openIME(device, wmHelper) + this.setRotation(testSpec.config.startRotation) + } + } + teardown { + test { + testApp.exit() + this.setRotation(Surface.ROTATION_0) + } + } + transitions { + device.pressHome() + wmHelper.waitForHomeActivityVisible() + wmHelper.waitImeWindowGone() + } + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + + @Presubmit + @Test + fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible() + + @Presubmit + @Test + fun imeAppWindowBecomesInvisible() = testSpec.imeAppWindowBecomesInvisible(testApp) + + @Presubmit + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, + Surface.ROTATION_0) + + @Presubmit + @Test + fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible() + + @Presubmit + @Test + fun imeAppLayerBecomesInvisible() = testSpec.imeAppLayerBecomesInvisible(testApp) + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun statusBarLayerRotatesScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.navBarLayerIsAlwaysVisible() + } + + @FlakyTest + @Test + fun navBarLayerIsAlwaysVisible_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerIsAlwaysVisible() + } + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerIsAlwaysVisible() + } + + @FlakyTest + @Test + fun statusBarLayerIsAlwaysVisible_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerIsAlwaysVisible() + } + + @Presubmit + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + } + + @FlakyTest + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + } companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - return FlickerTestRunnerFactory.getInstance() - .buildTest(instrumentation, repetitions = 5) { configuration -> - val testApp = ImeAppAutoFocusHelper(instrumentation, - configuration.startRotation) - withTestName { - buildTestTag(configuration) - } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - } - eachRun { - testApp.launchViaIntent(wmHelper) - testApp.openIME(device, wmHelper) - this.setRotation(configuration.startRotation) - } - } - teardown { - test { - testApp.exit() - this.setRotation(Surface.ROTATION_0) - } - } - transitions { - device.pressHome() - wmHelper.waitForHomeActivityVisible() - wmHelper.waitImeWindowGone() - } - assertions { - val isRotated = configuration.startRotation.isRotated() - - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(IME_WINDOW_TITLE)) - - imeWindowBecomesInvisible() - imeAppWindowBecomesInvisible(testApp) - } - - layersTrace { - noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0) - imeLayerBecomesInvisible() - imeAppLayerBecomesInvisible(testApp) - - if (!isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0) - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(IME_WINDOW_TITLE)) - } - } - } - - flaky { - layersTrace { - if (isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0) - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(IME_WINDOW_TITLE)) - } - } - } - } - } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(repetitions = 5) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt index 2bd5abb640e5..476708c42c4c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt @@ -16,13 +16,18 @@ package com.android.server.wm.flicker.ime +import android.app.Instrumentation +import android.platform.test.annotations.Postsubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.ImeAppHelper -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible @@ -33,10 +38,10 @@ import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEn import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation -import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -47,63 +52,97 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseImeWindowToAppTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { +class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = ImeAppHelper(instrumentation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + testApp.launchViaIntent() + this.setRotation(testSpec.config.startRotation) + } + eachRun { + testApp.openIME(device, wmHelper) + } + } + teardown { + test { + testApp.exit() + this.setRotation(Surface.ROTATION_0) + } + } + transitions { + testApp.closeIME(device, wmHelper) + } + } + } + + @Postsubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + + @Postsubmit + @Test + fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp) + + @Postsubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Postsubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Postsubmit + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation) + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation) + + @FlakyTest + @Test + fun statusBarLayerRotatesScales() = + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation) + + @Postsubmit + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + + @Postsubmit + @Test + fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible() + + @Postsubmit + @Test + fun imeAppLayerIsAlwaysVisible() = testSpec.imeAppLayerIsAlwaysVisible(testApp) companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = ImeAppHelper(instrumentation) - return FlickerTestRunnerFactory.getInstance() - .buildTest(instrumentation, repetitions = 5) { configuration -> - withTestName { buildTestTag(configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - testApp.launchViaIntent() - this.setRotation(configuration.startRotation) - } - eachRun { - testApp.openIME(device, wmHelper) - } - } - teardown { - test { - testApp.exit() - this.setRotation(Surface.ROTATION_0) - } - } - transitions { - testApp.closeIME(device, wmHelper) - } - assertions { - postsubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(IME_WINDOW_TITLE)) - imeAppWindowIsAlwaysVisible(testApp) - } - - layersTrace { - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - noUncoveredRegions(configuration.startRotation) - navBarLayerRotatesAndScales(configuration.startRotation) - statusBarLayerRotatesScales(configuration.startRotation) - visibleLayersShownMoreThanOneConsecutiveEntry() - imeLayerBecomesInvisible() - imeAppLayerIsAlwaysVisible(testApp) - } - } - } - } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(repetitions = 5) } } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt index 7b61bb58446c..ac140f505076 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt @@ -16,28 +16,33 @@ package com.android.server.wm.flicker.ime +import android.app.Instrumentation +import android.platform.test.annotations.Postsubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.ImeAppHelper -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.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation -import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry +import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import org.junit.Assume import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -47,90 +52,126 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseImeWindowToHomeTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { +class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = ImeAppHelper(instrumentation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + } + eachRun { + testApp.launchViaIntent(wmHelper) + this.setRotation(testSpec.config.startRotation) + testApp.openIME(device, wmHelper) + } + } + transitions { + device.pressHome() + wmHelper.waitForHomeActivityVisible() + wmHelper.waitImeWindowGone() + } + teardown { + eachRun { + device.pressHome() + wmHelper.waitForHomeActivityVisible() + } + test { + testApp.exit() + this.setRotation(Surface.ROTATION_0) + } + } + } + } + + @Postsubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + + @Postsubmit + @Test + fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible() + + @Postsubmit + @Test + fun imeAppWindowBecomesInvisible() = testSpec.imeAppWindowBecomesInvisible(testApp) + + @Postsubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Postsubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Postsubmit + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, + Surface.ROTATION_0) + + @Postsubmit + @Test + fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible() + + @Postsubmit + @Test + fun imeAppLayerBecomesInvisible() = testSpec.imeAppLayerBecomesInvisible(testApp) + + @Postsubmit + @Test + fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Postsubmit + @Test + fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun statusBarLayerRotatesScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = ImeAppHelper(instrumentation) - return FlickerTestRunnerFactory.getInstance() - .buildTest(instrumentation, repetitions = 5) { configuration -> - withTestName { buildTestTag(configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - } - eachRun { - testApp.launchViaIntent(wmHelper) - this.setRotation(configuration.startRotation) - testApp.openIME(device, wmHelper) - } - } - transitions { - device.pressHome() - wmHelper.waitForHomeActivityVisible() - wmHelper.waitImeWindowGone() - } - teardown { - eachRun { - device.pressHome() - wmHelper.waitForHomeActivityVisible() - } - test { - testApp.exit() - this.setRotation(Surface.ROTATION_0) - } - } - assertions { - val isRotated = configuration.startRotation.isRotated() - - postsubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(IME_WINDOW_TITLE)) - imeWindowBecomesInvisible() - imeAppWindowBecomesInvisible(testApp) - } - - layersTrace { - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - imeLayerBecomesInvisible() - imeAppLayerBecomesInvisible(testApp) - noUncoveredRegions(configuration.startRotation, - Surface.ROTATION_0) - - if (!isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0) - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) - } - } - } - - flaky { - layersTrace { - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(IME_WINDOW_TITLE)) - - if (isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0) - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) - } - } - } - } - } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(repetitions = 5) } } -}
\ No newline at end of file +} 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 cfdd8564128f..212644c04505 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,85 +17,75 @@ 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.FlickerTestParameter const val IME_WINDOW_TITLE = "InputMethod" -@JvmOverloads -fun LayersAssertionBuilder.imeLayerBecomesVisible(bugId: Int = 0) { - all("imeLayerBecomesVisible", bugId) { +fun FlickerTestParameter.imeLayerBecomesVisible() { + assertLayers { this.hidesLayer(IME_WINDOW_TITLE) - .then() - .showsLayer(IME_WINDOW_TITLE) + .then() + .showsLayer(IME_WINDOW_TITLE) } } -@JvmOverloads -fun LayersAssertionBuilder.imeLayerBecomesInvisible(bugId: Int = 0) { - all("imeLayerBecomesInvisible", bugId) { +fun FlickerTestParameter.imeLayerBecomesInvisible() { + assertLayers { this.showsLayer(IME_WINDOW_TITLE) - .then() - .hidesLayer(IME_WINDOW_TITLE) + .then() + .hidesLayer(IME_WINDOW_TITLE) } } -@JvmOverloads -fun LayersAssertionBuilder.imeAppLayerIsAlwaysVisible(testApp: IAppHelper, bugId: Int = 0) { - all("imeAppLayerIsAlwaysVisible", bugId) { +fun FlickerTestParameter.imeAppLayerIsAlwaysVisible(testApp: IAppHelper) { + assertLayers { this.showsLayer(testApp.getPackage()) } } -@JvmOverloads -fun WmAssertionBuilder.imeAppWindowIsAlwaysVisible(testApp: IAppHelper, bugId: Int = 0) { - all("imeAppWindowIsAlwaysVisible", bugId) { +fun FlickerTestParameter.imeAppWindowIsAlwaysVisible(testApp: IAppHelper) { + assertWm { this.showsAppWindowOnTop(testApp.getPackage()) } } -@JvmOverloads -fun WmAssertionBuilder.imeWindowBecomesVisible(bugId: Int = 0) { - all("imeWindowBecomesVisible", bugId) { +fun FlickerTestParameter.imeWindowBecomesVisible() { + assertWm { this.hidesNonAppWindow(IME_WINDOW_TITLE) - .then() - .showsNonAppWindow(IME_WINDOW_TITLE) + .then() + .showsNonAppWindow(IME_WINDOW_TITLE) } } -@JvmOverloads -fun WmAssertionBuilder.imeWindowBecomesInvisible(bugId: Int = 0) { - all("imeWindowBecomesInvisible", bugId) { +fun FlickerTestParameter.imeWindowBecomesInvisible() { + assertWm { this.showsNonAppWindow(IME_WINDOW_TITLE) - .then() - .hidesNonAppWindow(IME_WINDOW_TITLE) + .then() + .hidesNonAppWindow(IME_WINDOW_TITLE) } } -@JvmOverloads -fun WmAssertionBuilder.imeAppWindowBecomesVisible(windowName: String, bugId: Int = 0) { - all("imeAppWindowBecomesVisible", bugId) { +fun FlickerTestParameter.imeAppWindowBecomesVisible(windowName: String) { + assertWm { this.hidesAppWindow(windowName) - .then() - .showsAppWindow(windowName) + .then() + .showsAppWindow(windowName) } } -@JvmOverloads -fun WmAssertionBuilder.imeAppWindowBecomesInvisible(testApp: IAppHelper, bugId: Int = 0) { - all("imeAppWindowBecomesInvisible", bugId) { +fun FlickerTestParameter.imeAppWindowBecomesInvisible(testApp: IAppHelper) { + assertWm { this.showsAppWindowOnTop(testApp.getPackage()) - .then() - .appWindowNotOnTop(testApp.getPackage()) + .then() + .appWindowNotOnTop(testApp.getPackage()) } } -@JvmOverloads -fun LayersAssertionBuilder.imeAppLayerBecomesInvisible(testApp: IAppHelper, bugId: Int = 0) { - all("imeAppLayerBecomesInvisible", bugId) { +fun FlickerTestParameter.imeAppLayerBecomesInvisible(testApp: IAppHelper) { + assertLayers { this.skipUntilFirstAssertion() - .showsLayer(testApp.getPackage()) - .then() - .hidesLayer(testApp.getPackage()) + .showsLayer(testApp.getPackage()) + .then() + .hidesLayer(testApp.getPackage()) } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt index 9e94d6e49527..c7a5178a6693 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt @@ -16,13 +16,17 @@ package com.android.server.wm.flicker.ime +import android.app.Instrumentation +import android.platform.test.annotations.Postsubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.helpers.ImeAppHelper -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible @@ -32,14 +36,16 @@ import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEn import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.appWindowAlwaysVisibleOnTop -import com.android.server.wm.flicker.helpers.isRotated +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.layerAlwaysVisible import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import org.junit.Assume import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -50,80 +56,119 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenImeWindowTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { +class OpenImeWindowTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = ImeAppHelper(instrumentation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + testApp.launchViaIntent(wmHelper) + this.setRotation(testSpec.config.startRotation) + } + } + transitions { + testApp.openIME(device, wmHelper) + } + teardown { + eachRun { + testApp.closeIME(device, wmHelper) + } + test { + testApp.exit() + this.setRotation(Surface.ROTATION_0) + } + } + } + } + + @Postsubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + fun imeWindowBecomesVisible() = testSpec.imeWindowBecomesVisible() + + @Postsubmit + @Test + fun appWindowAlwaysVisibleOnTop() = testSpec.appWindowAlwaysVisibleOnTop(testApp.`package`) + + @Postsubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Postsubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Postsubmit + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation) + + @Postsubmit + @Test + fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible() + + @Postsubmit + @Test + fun layerAlwaysVisible() = testSpec.layerAlwaysVisible(testApp.`package`) + + @Postsubmit + @Test + fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation) + } + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation) + } + + @Postsubmit + @Test + fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation) + } + + @FlakyTest + @Test + fun statusBarLayerRotatesScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation) + } + + @FlakyTest + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + + @FlakyTest + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = ImeAppHelper(instrumentation) - return FlickerTestRunnerFactory.getInstance() - .buildTest(instrumentation, repetitions = 5) { configuration -> - withTestName { buildTestTag(configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - testApp.launchViaIntent(wmHelper) - this.setRotation(configuration.startRotation) - } - } - transitions { - testApp.openIME(device, wmHelper) - } - teardown { - eachRun { - testApp.closeIME(device, wmHelper) - } - test { - testApp.exit() - this.setRotation(Surface.ROTATION_0) - } - } - assertions { - val isRotated = configuration.startRotation.isRotated() - - postsubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - - imeWindowBecomesVisible() - appWindowAlwaysVisibleOnTop(testApp.`package`) - } - - layersTrace { - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - noUncoveredRegions(configuration.startRotation) - imeLayerBecomesVisible() - layerAlwaysVisible(testApp.`package`) - - if (!isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation) - statusBarLayerRotatesScales(configuration.startRotation) - } - } - } - - flaky { - windowManagerTrace { - visibleWindowsShownMoreThanOneConsecutiveEntry() - } - layersTrace { - visibleLayersShownMoreThanOneConsecutiveEntry() - - if (isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation) - statusBarLayerRotatesScales(configuration.startRotation) - } - } - } - } - } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(repetitions = 5) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt index 52549a23c9bb..0cd5d7999a58 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt @@ -16,14 +16,18 @@ package com.android.server.wm.flicker.ime +import android.app.Instrumentation +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper import com.android.server.wm.flicker.helpers.reopenAppFromOverview -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible @@ -31,18 +35,20 @@ import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.endRotation -import com.android.server.wm.flicker.helpers.isRotated import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.testapp.ActivityOptions +import org.junit.Assume import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -53,89 +59,132 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ReOpenImeWindowTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { +class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation) + private val testAppComponentName = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + testApp.launchViaIntent(wmHelper) + testApp.openIME(device, wmHelper) + } + eachRun { + device.pressRecentApps() + wmHelper.waitImeWindowGone() + wmHelper.waitForAppTransitionIdle() + this.setRotation(testSpec.config.startRotation) + } + } + transitions { + device.reopenAppFromOverview(wmHelper) + wmHelper.waitImeWindowShown() + } + teardown { + test { + this.setRotation(Surface.ROTATION_0) + testApp.exit() + } + } + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + + @Presubmit + @Test + fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible() + + @Presubmit + @Test + fun imeWindowBecomesVisible() = testSpec.imeWindowBecomesVisible() + + @Presubmit + @Test + fun imeAppWindowBecomesVisible() = + testSpec.imeAppWindowBecomesVisible(testAppComponentName.className) + + @Presubmit + @Test + // During testing the launcher is always in portrait mode + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, + testSpec.config.endRotation) + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible() + + @Presubmit + @Test + fun appLayerReplacesWallpaperLayer() = + testSpec.appLayerReplacesWallpaperLayer(testAppComponentName.className) + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation) + } + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation) + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation) + } + + @FlakyTest + @Test + fun statusBarLayerRotatesScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation) + } + + @FlakyTest + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testAppComponentName = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME - return FlickerTestRunnerFactory.getInstance() - .buildTest(instrumentation, repetitions = 1) { configuration -> - val testApp = ImeAppAutoFocusHelper(instrumentation, - configuration.startRotation) - withTestName { buildTestTag(configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - testApp.launchViaIntent(wmHelper) - testApp.openIME(device, wmHelper) - } - eachRun { - device.pressRecentApps() - wmHelper.waitImeWindowGone() - wmHelper.waitForAppTransitionIdle() - this.setRotation(configuration.startRotation) - } - } - transitions { - device.reopenAppFromOverview(wmHelper) - wmHelper.waitImeWindowShown() - } - teardown { - test { - this.setRotation(Surface.ROTATION_0) - testApp.exit() - } - } - assertions { - val isRotated = configuration.startRotation.isRotated() - - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry() - - imeWindowBecomesVisible() - imeAppWindowBecomesVisible(testAppComponentName.className) - wallpaperWindowBecomesInvisible() - } - - layersTrace { - noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation) - statusBarLayerIsAlwaysVisible() - navBarLayerIsAlwaysVisible() - imeLayerBecomesVisible() - appLayerReplacesWallpaperLayer(testAppComponentName.className) - - if (!isRotated) { - navBarLayerRotatesAndScales(Surface.ROTATION_0, - configuration.endRotation) - statusBarLayerRotatesScales(Surface.ROTATION_0, - configuration.endRotation) - } - } - } - - flaky { - layersTrace { - visibleLayersShownMoreThanOneConsecutiveEntry() - - if (isRotated) { - navBarLayerRotatesAndScales(Surface.ROTATION_0, - configuration.endRotation) - statusBarLayerRotatesScales(Surface.ROTATION_0, - configuration.endRotation) - } - } - } - } - } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(repetitions = 1) } } -}
\ No newline at end of file +} 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 be3fa5fa3cdf..130860d31ac1 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,12 +17,12 @@ 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.FlickerTestParameter -fun WmAssertionBuilder.appWindowReplacesLauncherAsTopWindow(testApp: IAppHelper, bugId: Int = 0) { - all("appWindowReplacesLauncherAsTopWindow", bugId) { +fun FlickerTestParameter.appWindowReplacesLauncherAsTopWindow(testApp: IAppHelper) { + assertWm { this.showsAppWindowOnTop("Launcher") - .then() - .showsAppWindowOnTop("Snapshot", testApp.getPackage()) + .then() + .showsAppWindowOnTop("Snapshot", testApp.getPackage()) } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt index 0ec0b04339cd..74f002d67229 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt @@ -16,14 +16,17 @@ package com.android.server.wm.flicker.launch +import android.app.Instrumentation +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunnerFactory -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.endRotation +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.focusChanges -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible @@ -39,9 +42,11 @@ import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEn import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.helpers.isRotated +import org.junit.Assume import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -52,85 +57,122 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppColdTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { +class OpenAppColdTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = SimpleAppHelper(instrumentation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + } + eachRun { + this.setRotation(testSpec.config.startRotation) + } + } + transitions { + testApp.launchViaIntent(wmHelper) + // wmHelper.waitForFullScreenApp(testApp.component) + } + teardown { + eachRun { + testApp.exit() + wmHelper.waitForAppTransitionIdle() + this.setRotation(Surface.ROTATION_0) + } + } + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + + @Presubmit + @Test + fun appWindowReplacesLauncherAsTopWindow() = + testSpec.appWindowReplacesLauncherAsTopWindow(testApp) + + @Presubmit + @Test + fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible() + + @Presubmit + @Test + // During testing the launcher is always in portrait mode + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, + Surface.ROTATION_0) + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun appLayerReplacesWallpaperLayer() = + testSpec.appLayerReplacesWallpaperLayer(testApp.`package`) + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun statusBarLayerRotatesScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Presubmit + @Test + fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`) + + @FlakyTest + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = SimpleAppHelper(instrumentation) - return FlickerTestRunnerFactory.getInstance() - .buildTest(instrumentation) { configuration -> - withTestName { buildTestTag(configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - } - eachRun { - this.setRotation(configuration.startRotation) - } - } - transitions { - testApp.open() - wmHelper.waitForFullScreenApp(testApp.component) - } - teardown { - eachRun { - testApp.exit() - wmHelper.waitForAppTransitionIdle() - this.setRotation(Surface.ROTATION_0) - } - } - assertions { - val isRotated = configuration.startRotation.isRotated() - - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry() - appWindowReplacesLauncherAsTopWindow(testApp) - wallpaperWindowBecomesInvisible() - } - - layersTrace { - // During testing the launcher is always in portrait mode - noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation) - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - appLayerReplacesWallpaperLayer(testApp.`package`) - - if (!isRotated) { - navBarLayerRotatesAndScales(Surface.ROTATION_0, - configuration.endRotation) - statusBarLayerRotatesScales(Surface.ROTATION_0, - configuration.endRotation) - } - } - - eventLog { - focusChanges("NexusLauncherActivity", testApp.`package`) - } - } - - flaky { - layersTrace { - visibleLayersShownMoreThanOneConsecutiveEntry() - - if (isRotated) { - navBarLayerRotatesAndScales(Surface.ROTATION_0, - configuration.endRotation) - statusBarLayerRotatesScales(Surface.ROTATION_0, - configuration.endRotation) - } - } - } - } - } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt index 5fb050927dad..e2a258aea239 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt @@ -16,17 +16,22 @@ package com.android.server.wm.flicker.launch +import android.app.Instrumentation +import android.platform.test.annotations.Postsubmit +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunnerFactory -import com.android.server.wm.flicker.FlickerTestRunner +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.focusChanges import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.helpers.reopenAppFromOverview -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible @@ -40,9 +45,11 @@ import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.helpers.isRotated +import org.junit.Assume import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -53,97 +60,115 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppFromOverviewTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { +class OpenAppFromOverviewTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = SimpleAppHelper(instrumentation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + testApp.launchViaIntent(wmHelper) + } + eachRun { + device.pressHome() + wmHelper.waitForAppTransitionIdle() + device.pressRecentApps() + wmHelper.waitForAppTransitionIdle() + this.setRotation(testSpec.config.startRotation) + } + } + transitions { + device.reopenAppFromOverview(wmHelper) + wmHelper.waitForFullScreenApp(testApp.component) + } + teardown { + test { + testApp.exit() + } + } + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Test + fun appWindowReplacesLauncherAsTopWindow() = + testSpec.appWindowReplacesLauncherAsTopWindow(testApp) + + @Test + fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible() + + @Presubmit + @Test + fun appLayerReplacesWallpaperLayer() = + testSpec.appLayerReplacesWallpaperLayer(testApp.`package`) + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerIsAlwaysVisible() + } + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerIsAlwaysVisible() + } + + @Presubmit + @Test + fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`) + + @Postsubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + + @FlakyTest + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + + @FlakyTest(bugId = 141361128) + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(Surface.ROTATION_0, + testSpec.config.endRotation) + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = SimpleAppHelper(instrumentation) - return FlickerTestRunnerFactory.getInstance() - .buildTest(instrumentation, repetitions = 5) { configuration -> - withTestName { buildTestTag(configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - testApp.launchViaIntent(wmHelper) - } - eachRun { - device.pressHome() - wmHelper.waitForAppTransitionIdle() - device.pressRecentApps() - wmHelper.waitForAppTransitionIdle() - this.setRotation(configuration.startRotation) - } - } - transitions { - device.reopenAppFromOverview(wmHelper) - wmHelper.waitForFullScreenApp(testApp.component) - } - teardown { - test { - testApp.exit() - } - } - assertions { - val isRotated = configuration.startRotation.isRotated() - - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - appWindowReplacesLauncherAsTopWindow(testApp) - wallpaperWindowBecomesInvisible() - } - - layersTrace { - appLayerReplacesWallpaperLayer(testApp.`package`) - - if (!isRotated) { - navBarLayerRotatesAndScales(Surface.ROTATION_0, - configuration.endRotation) - statusBarLayerRotatesScales(Surface.ROTATION_0, - configuration.endRotation) - } else { - statusBarLayerIsAlwaysVisible() - navBarLayerIsAlwaysVisible() - } - } - - eventLog { - focusChanges("NexusLauncherActivity", testApp.`package`) - } - } - - postsubmit { - windowManagerTrace { - visibleWindowsShownMoreThanOneConsecutiveEntry() - } - } - - flaky { - layersTrace { - visibleLayersShownMoreThanOneConsecutiveEntry() - noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation, - bugId = 141361128) - - if (isRotated) { - navBarLayerRotatesAndScales(Surface.ROTATION_0, - configuration.endRotation) - statusBarLayerRotatesScales(Surface.ROTATION_0, - configuration.endRotation) - } else { - statusBarLayerIsAlwaysVisible() - navBarLayerIsAlwaysVisible() - } - } - } - } - } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(repetitions = 5) } } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt index 1f375a5cdea8..386dafc590af 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt @@ -16,14 +16,17 @@ package com.android.server.wm.flicker.launch +import android.app.Instrumentation +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunnerFactory -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.endRotation +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.focusChanges -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible @@ -36,12 +39,13 @@ import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.helpers.isRotated +import org.junit.Assume import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -52,89 +56,122 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppWarmTest(testSpec: FlickerTestRunnerFactory.TestSpec) : FlickerTestRunner(testSpec) { +class OpenAppWarmTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = SimpleAppHelper(instrumentation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + testApp.launchViaIntent(wmHelper) + // wmHelper.waitForFullScreenApp(testApp.component) + } + eachRun { + device.pressHome() + wmHelper.waitForHomeActivityVisible() + this.setRotation(testSpec.config.startRotation) + } + } + transitions { + testApp.launchViaIntent(wmHelper) + wmHelper.waitForFullScreenApp(testApp.component) + } + teardown { + eachRun { + this.setRotation(Surface.ROTATION_0) + } + test { + testApp.exit() + } + } + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + + @Presubmit + @Test + fun appWindowReplacesLauncherAsTopWindow() = + testSpec.appWindowReplacesLauncherAsTopWindow(testApp) + + @Presubmit + @Test + fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible() + + @Presubmit + @Test + // During testing the launcher is always in portrait mode + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, + Surface.ROTATION_0) + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun appLayerReplacesWallpaperLayer() = + testSpec.appLayerReplacesWallpaperLayer(testApp.`package`) + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun statusBarLayerRotatesScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Presubmit + @Test + fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`) + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = SimpleAppHelper(instrumentation) - return FlickerTestRunnerFactory.getInstance() - .buildTest(instrumentation) { configuration -> - withTestName { buildTestTag(configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - testApp.open() - wmHelper.waitForFullScreenApp(testApp.component) - } - eachRun { - device.pressHome() - wmHelper.waitForHomeActivityVisible() - this.setRotation(configuration.startRotation) - } - } - transitions { - testApp.open() - wmHelper.waitForFullScreenApp(testApp.component) - } - teardown { - eachRun { - this.setRotation(Surface.ROTATION_0) - } - test { - testApp.exit() - } - } - assertions { - val isRotated = configuration.startRotation.isRotated() - - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry() - - appWindowReplacesLauncherAsTopWindow(testApp) - wallpaperWindowBecomesInvisible() - } - - layersTrace { - // During testing the launcher is always in portrait mode - noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation) - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - appLayerReplacesWallpaperLayer(testApp.`package`) - - if (!isRotated) { - navBarLayerRotatesAndScales(Surface.ROTATION_0, - configuration.endRotation) - statusBarLayerRotatesScales(Surface.ROTATION_0, - configuration.endRotation) - } - } - - eventLog { - focusChanges("NexusLauncherActivity", testApp.`package`) - } - } - - flaky { - layersTrace { - visibleLayersShownMoreThanOneConsecutiveEntry() - - if (isRotated) { - navBarLayerRotatesAndScales(Surface.ROTATION_0, - configuration.endRotation) - statusBarLayerRotatesScales(Surface.ROTATION_0, - configuration.endRotation) - } - } - } - } - } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt index 7bfdd96b9af8..3cc509fe2b8e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt @@ -17,28 +17,27 @@ package com.android.server.wm.flicker.rotation import android.os.Bundle +import android.platform.test.annotations.Presubmit +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory -import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.focusDoesNotChange import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.helpers.StandardAppHelper -import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.startRotation -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry +import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -49,78 +48,95 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class ChangeAppRotationTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : RotationTransition(InstrumentationRegistry.getInstrumentation()) { - override val testApp: StandardAppHelper - get() = SimpleAppHelper(instrumentation) + testSpec: FlickerTestParameter +) : RotationTransition(testSpec) { + override val testApp = SimpleAppHelper(instrumentation) + override fun getAppLaunchParams(configuration: Bundle): Map<String, String> = emptyMap() + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + + @Presubmit + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, + testSpec.config.endRotation, allStates = false) + + @Presubmit + @Test + fun screenshotLayerBecomesInvisible() { + testSpec.assertLayers { + this.showsLayer(testApp.getPackage()) + .then() + .showsLayer(SCREENSHOT_LAYER) + .then() + .showsLayer(testApp.getPackage()) + } + } + + @FlakyTest(bugId = 140855415) + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @FlakyTest(bugId = 140855415) + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @FlakyTest(bugId = 140855415) + @Test + fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales( + testSpec.config.startRotation, testSpec.config.endRotation) + + @FlakyTest(bugId = 140855415) + @Test + fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales( + testSpec.config.startRotation, testSpec.config.endRotation) + + @FlakyTest(bugId = 140855415) + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + + @FlakyTest(bugId = 140855415) + @Test + fun appLayerRotates_StartingPos() { + testSpec.assertLayersStart { + this.hasVisibleRegion(testApp.getPackage(), startingPos) + } + } + + @FlakyTest(bugId = 140855415) + @Test + fun appLayerRotates_EndingPos() { + testSpec.assertLayersEnd { + this.hasVisibleRegion(testApp.getPackage(), endingPos) + } + } - override fun getAppLaunchParams(configuration: Bundle): Map<String, String> = emptyMap() + @FlakyTest(bugId = 151179149) + @Test + fun focusDoesNotChange() = testSpec.focusDoesNotChange() + companion object { private const val SCREENSHOT_LAYER = "RotationLayer" - @Parameterized.Parameters(name = "{0}1}") + @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { buildTestTag(configuration) } - assertions { - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry() - } - - layersTrace { - noUncoveredRegions(configuration.startRotation, - configuration.endRotation, allStates = false) - - all("screenshotLayerBecomesInvisible") { - this.showsLayer(testApp.getPackage()) - .then() - .showsLayer(SCREENSHOT_LAYER) - .then() - .showsLayer(testApp.getPackage()) - } - } - } - - flaky { - layersTrace { - navBarLayerIsAlwaysVisible(bugId = 140855415) - statusBarLayerIsAlwaysVisible(bugId = 140855415) - navBarLayerRotatesAndScales(configuration.startRotation, - configuration.endRotation, bugId = 140855415) - statusBarLayerRotatesScales(configuration.startRotation, - configuration.endRotation, bugId = 140855415) - visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 140855415) - - val startingPos = WindowUtils.getDisplayBounds( - configuration.startRotation) - val endingPos = WindowUtils.getDisplayBounds( - configuration.endRotation) - - start("appLayerRotates_StartingPos", bugId = 140855415) { - this.hasVisibleRegion(testApp.getPackage(), startingPos) - } - - end("appLayerRotates_EndingPos", bugId = 140855415) { - this.hasVisibleRegion(testApp.getPackage(), endingPos) - } - } - - eventLog { - focusDoesNotChange(bugId = 151179149) - } - } - } - } - - return FlickerTestRunnerFactory.getInstance() - .buildRotationTest(instrumentation, transition, testSpec, repetitions = 5) + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigRotationTests(repetitions = 5) } } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt index b871e949cb19..04ab84dfcd8e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt @@ -18,29 +18,39 @@ package com.android.server.wm.flicker.rotation import android.app.Instrumentation import android.os.Bundle +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.helpers.StandardAppHelper +import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation -abstract class RotationTransition(protected val instrumentation: Instrumentation) { - abstract val testApp: StandardAppHelper - abstract fun getAppLaunchParams(configuration: Bundle): Map<String, String> +abstract class RotationTransition(protected val testSpec: FlickerTestParameter) { + protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + protected val startingPos get() = WindowUtils.getDisplayBounds(testSpec.config.startRotation) + protected val endingPos get() = WindowUtils.getDisplayBounds(testSpec.config.endRotation) - protected open val transition: FlickerBuilder.(Bundle) -> Unit - get() = { configuration -> - repeat { configuration.repetitions } + protected abstract val testApp: StandardAppHelper + protected abstract fun getAppLaunchParams(configuration: Bundle): Map<String, String> + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } setup { test { device.wakeUpAndGoToHomeScreen() - val extras = getAppLaunchParams(configuration) + val extras = getAppLaunchParams(testSpec.config) testApp.launchViaIntent(wmHelper, stringExtras = extras) } eachRun { - this.setRotation(configuration.startRotation) + this.setRotation(testSpec.config.startRotation) } } teardown { @@ -49,7 +59,8 @@ abstract class RotationTransition(protected val instrumentation: Instrumentation } } transitions { - this.setRotation(configuration.endRotation) + this.setRotation(testSpec.config.endRotation) } } + } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt index 78614640a372..ef1aeadb31bc 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt @@ -17,31 +17,30 @@ package com.android.server.wm.flicker.rotation import android.os.Bundle +import android.platform.test.annotations.Presubmit +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.appWindowAlwaysVisibleOnTop import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.focusDoesNotChange -import com.android.server.wm.flicker.appWindowAlwaysVisibleOnTop -import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper -import com.android.server.wm.flicker.helpers.StandardAppHelper import com.android.server.wm.flicker.layerAlwaysVisible -import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.testapp.ActivityOptions +import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry +import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -52,116 +51,108 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class SeamlessAppRotationTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : RotationTransition(InstrumentationRegistry.getInstrumentation()) { - override val testApp: StandardAppHelper - get() = SeamlessRotationAppHelper(instrumentation) + testSpec: FlickerTestParameter +) : RotationTransition(testSpec) { + override val testApp = SeamlessRotationAppHelper(instrumentation) + + override fun getAppLaunchParams(configuration: Bundle): Map<String, String> = mapOf( + ActivityOptions.EXTRA_STARVE_UI_THREAD to configuration.starveUiThread.toString() + ) + + @Presubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + + @Presubmit + @Test + fun appWindowAlwaysVisibleOnTop() = testSpec.appWindowAlwaysVisibleOnTop(testApp.`package`) + + @Presubmit + @Test + fun layerAlwaysVisible() = testSpec.layerAlwaysVisible(testApp.`package`) + + @FlakyTest(bugId = 140855415) + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @FlakyTest(bugId = 140855415) + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @FlakyTest(bugId = 140855415) + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @FlakyTest(bugId = 140855415) + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @FlakyTest(bugId = 147659548) + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, + testSpec.config.endRotation, allStates = false) + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales( + testSpec.config.startRotation, testSpec.config.endRotation) + + @FlakyTest + @Test + fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales( + testSpec.config.startRotation, testSpec.config.endRotation) + + @FlakyTest + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + + @FlakyTest(bugId = 147659548) + @Test + fun appLayerRotates() { + testSpec.assertLayers { + this.hasVisibleRegion(testApp.`package`, startingPos) + } + } - override fun getAppLaunchParams(configuration: Bundle): Map<String, String> = mapOf( - ActivityOptions.EXTRA_STARVE_UI_THREAD to configuration.starveUiThread.toString() - ) + @FlakyTest(bugId = 151179149) + @Test + fun focusDoesNotChange() = testSpec.focusDoesNotChange() - private val testFactory = FlickerTestRunnerFactory.getInstance() + companion object { + private val testFactory = FlickerTestParameterFactory.getInstance() private val Bundle.starveUiThread get() = this.getBoolean(ActivityOptions.EXTRA_STARVE_UI_THREAD, false) - private fun Bundle.createConfig(starveUiThread: Boolean): Bundle { - val config = this.deepCopy() + private fun FlickerTestParameter.createConfig(starveUiThread: Boolean): Bundle { + val config = this.config.deepCopy() config.putBoolean(ActivityOptions.EXTRA_STARVE_UI_THREAD, starveUiThread) return config } @JvmStatic - private fun getConfigurations(): List<Bundle> { - return testFactory.getConfigRotationTests().flatMap { + private fun getConfigurations(): List<FlickerTestParameter> { + return testFactory.getConfigRotationTests(repetitions = 2).flatMap { val defaultRun = it.createConfig(starveUiThread = false) val busyUiRun = it.createConfig(starveUiThread = true) - listOf(defaultRun, busyUiRun) + listOf( + FlickerTestParameter(defaultRun), + FlickerTestParameter(busyUiRun, + name = "${FlickerTestParameter.defaultName(busyUiRun)}_BUSY_UI_THREAD" + ) + ) } } @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<Array<Any>> { - val configurations = getConfigurations() - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - val extra = if (configuration.starveUiThread) { - "BUSY_UI_THREAD" - } else { - "" - } - buildTestTag(configuration, extraInfo = extra) - } - assertions { - val startingBounds = WindowUtils.getDisplayBounds(configuration.startRotation) - val endingBounds = WindowUtils.getDisplayBounds(configuration.endRotation) - - presubmit { - windowManagerTrace { - visibleWindowsShownMoreThanOneConsecutiveEntry() - appWindowAlwaysVisibleOnTop(testApp.`package`) - } - - layersTrace { - layerAlwaysVisible(testApp.`package`) - } - } - - flaky { - windowManagerTrace { - navBarWindowIsAlwaysVisible(bugId = 140855415) - statusBarWindowIsAlwaysVisible(bugId = 140855415) - } - - layersTrace { - navBarLayerIsAlwaysVisible(bugId = 140855415) - statusBarLayerIsAlwaysVisible(bugId = 140855415) - noUncoveredRegions(configuration.startRotation, - configuration.endRotation, allStates = false, bugId = 147659548) - navBarLayerRotatesAndScales(configuration.startRotation, - configuration.endRotation) - statusBarLayerRotatesScales(configuration.startRotation, - configuration.endRotation) - visibleLayersShownMoreThanOneConsecutiveEntry() - - all("appLayerRotates", bugId = 147659548) { - if (startingBounds == endingBounds) { - this.hasVisibleRegion( - testApp.`package`, startingBounds) - } else { - this.hasVisibleRegion(testApp.`package`, - startingBounds) - .then() - .hasVisibleRegion(testApp.`package`, - endingBounds) - } - } - - all("noUncoveredRegions", bugId = 147659548) { - if (startingBounds == endingBounds) { - this.coversAtLeastRegion(startingBounds) - } else { - this.coversAtLeastRegion(startingBounds) - .then() - .coversAtLeastRegion(endingBounds) - } - } - } - - eventLog { - focusDoesNotChange(bugId = 151179149) - } - } - } - } - - return testFactory.buildRotationTest(instrumentation, transition, testSpec, - deviceConfigurations = configurations, repetitions = 2) + fun getParams(): Collection<FlickerTestParameter> { + return getConfigurations() } } -}
\ No newline at end of file +} diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp index a72b07c45dd8..335c8d0127eb 100644 --- a/tests/Input/Android.bp +++ b/tests/Input/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test { name: "InputTests", srcs: ["src/**/*.kt"], diff --git a/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt b/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt index f919a3eaf271..c19e5cc34611 100644 --- a/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt +++ b/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt @@ -62,15 +62,12 @@ class ViewFrameInfoTest { fun testUpdateFrameInfoFromViewFrameInfo() { val frameInfo = FrameInfo() // By default, all values should be zero - assertThat(frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT]).isEqualTo(0) - assertThat(frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT]).isEqualTo(0) + // TODO(b/169866723): Use InputEventAssigner and assert INPUT_EVENT_ID assertThat(frameInfo.frameInfo[FrameInfo.FLAGS]).isEqualTo(0) assertThat(frameInfo.frameInfo[FrameInfo.DRAW_START]).isEqualTo(0) // The values inside FrameInfo should match those from ViewFrameInfo after we update them mViewFrameInfo.populateFrameInfo(frameInfo) - assertThat(frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT]).isEqualTo(10) - assertThat(frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT]).isEqualTo(20) assertThat(frameInfo.frameInfo[FrameInfo.FLAGS]).isEqualTo( FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED) assertThat(frameInfo.frameInfo[FrameInfo.DRAW_START]).isGreaterThan(mTimeStarted) diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp index 95044dc5e7bb..6e1cef496f40 100644 --- a/tests/RollbackTest/Android.bp +++ b/tests/RollbackTest/Android.bp @@ -105,6 +105,7 @@ apex { key: "com.android.apex.apkrollback.test.key", apps: ["TestAppAv1"], installable: false, + updatable: false, } apex { @@ -115,6 +116,7 @@ apex { key: "com.android.apex.apkrollback.test.key", apps: ["TestAppAv2"], installable: false, + updatable: false, } apex { @@ -125,4 +127,5 @@ apex { key: "com.android.apex.apkrollback.test.key", apps: ["TestAppACrashingV2"], installable: false, + updatable: false, } diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java index 0508125edfc8..0bb0337b3b09 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java @@ -35,7 +35,6 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.cts.install.lib.Install; import com.android.cts.install.lib.InstallUtils; import com.android.cts.install.lib.TestApp; -import com.android.cts.install.lib.Uninstall; import com.android.cts.rollback.lib.Rollback; import com.android.cts.rollback.lib.RollbackUtils; import com.android.internal.R; @@ -89,7 +88,6 @@ public class StagedRollbackTest { */ @Test public void testBadApkOnly_Phase1_Install() throws Exception { - Uninstall.packages(TestApp.A); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1); Install.single(TestApp.A1).commit(); @@ -149,7 +147,6 @@ public class StagedRollbackTest { */ @Test public void testNativeWatchdogTriggersRollback_Phase1_Install() throws Exception { - Uninstall.packages(TestApp.A); Install.single(TestApp.A1).commit(); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); @@ -183,7 +180,6 @@ public class StagedRollbackTest { */ @Test public void testNativeWatchdogTriggersRollbackForAll_Phase1_InstallA() throws Exception { - Uninstall.packages(TestApp.A); Install.single(TestApp.A1).commit(); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); @@ -201,7 +197,6 @@ public class StagedRollbackTest { TestApp.A)).isNotNull(); // Install another package with rollback - Uninstall.packages(TestApp.B); Install.single(TestApp.B1).commit(); assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1); @@ -238,7 +233,6 @@ public class StagedRollbackTest { @Test public void testPreviouslyAbandonedRollbacks_Phase1_InstallAndAbandon() throws Exception { - Uninstall.packages(TestApp.A); Install.single(TestApp.A1).commit(); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); @@ -265,7 +259,6 @@ public class StagedRollbackTest { public void testPreviouslyAbandonedRollbacks_Phase3_VerifyRollback() throws Exception { assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); InstallUtils.processUserData(TestApp.A); - Uninstall.packages(TestApp.A); } private static String getModuleMetadataPackageName() { @@ -301,7 +294,6 @@ public class StagedRollbackTest { @Test public void testRollbackDataPolicy_Phase1_Install() throws Exception { - Uninstall.packages(TestApp.A, TestApp.B, TestApp.C); Install.multi(TestApp.A1, TestApp.B1, TestApp.C1).commit(); // Write user data version = 1 InstallUtils.processUserData(TestApp.A); diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java index 65fb7b6c8cc6..304567a34ed3 100644 --- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java +++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java @@ -99,10 +99,16 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex"); runPhase("expireRollbacks"); mLogger.start(getDevice()); + getDevice().uninstallPackage("com.android.cts.install.lib.testapp.A"); + getDevice().uninstallPackage("com.android.cts.install.lib.testapp.B"); + getDevice().uninstallPackage("com.android.cts.install.lib.testapp.C"); } @After public void tearDown() throws Exception { + getDevice().uninstallPackage("com.android.cts.install.lib.testapp.A"); + getDevice().uninstallPackage("com.android.cts.install.lib.testapp.B"); + getDevice().uninstallPackage("com.android.cts.install.lib.testapp.C"); mLogger.stop(); runPhase("expireRollbacks"); deleteFiles("/system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex", @@ -283,7 +289,6 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { */ @Test public void testRollbackApexWithApk() throws Exception { - getDevice().uninstallPackage("com.android.cts.install.lib.testapp.A"); pushTestApex(); runPhase("testRollbackApexWithApk_Phase1_Install"); getDevice().reboot(); @@ -297,7 +302,6 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { */ @Test public void testRollbackApexWithApkCrashing() throws Exception { - getDevice().uninstallPackage("com.android.cts.install.lib.testapp.A"); pushTestApex(); // Install an apex with apk that crashes diff --git a/tests/SilkFX/Android.bp b/tests/SilkFX/Android.bp index 92e3efa7fd55..088d9a2d7f41 100644 --- a/tests/SilkFX/Android.bp +++ b/tests/SilkFX/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test { name: "SilkFX", srcs: ["**/*.java", "**/*.kt"], diff --git a/tests/SurfaceViewBufferTests/Android.bp b/tests/SurfaceViewBufferTests/Android.bp index 48031de15f54..dc75f00e7cdc 100644 --- a/tests/SurfaceViewBufferTests/Android.bp +++ b/tests/SurfaceViewBufferTests/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test { name: "SurfaceViewBufferTests", srcs: ["**/*.java","**/*.kt"], diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp index 43a5078c3c24..ee24d48f0ed5 100644 --- a/tests/UpdatableSystemFontTest/Android.bp +++ b/tests/UpdatableSystemFontTest/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + java_test_host { name: "UpdatableSystemFontTest", srcs: ["src/**/*.java"], diff --git a/tests/UpdatableSystemFontTest/testdata/Android.bp b/tests/UpdatableSystemFontTest/testdata/Android.bp index 1296699e3c9f..f744d5dd2b51 100644 --- a/tests/UpdatableSystemFontTest/testdata/Android.bp +++ b/tests/UpdatableSystemFontTest/testdata/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + filegroup { name: "UpdatableSystemFontTestKeyPem", srcs: ["UpdatableSystemFontTestKey.pem"], diff --git a/tests/benchmarks/internal/Android.bp b/tests/benchmarks/internal/Android.bp index 9c34eaf2af01..74ed7a34f626 100644 --- a/tests/benchmarks/internal/Android.bp +++ b/tests/benchmarks/internal/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test { name: "InternalBenchTests", srcs: ["src/**/*.java"], @@ -23,4 +32,3 @@ android_test { platform_apis: true, certificate: "platform" } - diff --git a/tests/net/common/java/android/net/NetworkStackTest.java b/tests/net/common/java/android/net/NetworkStackTest.java index a99aa0106655..f8f9c72374ad 100644 --- a/tests/net/common/java/android/net/NetworkStackTest.java +++ b/tests/net/common/java/android/net/NetworkStackTest.java @@ -15,20 +15,8 @@ */ package android.net; -import static android.Manifest.permission.NETWORK_STACK; -import static android.content.pm.PackageManager.PERMISSION_DENIED; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; -import static android.net.NetworkStack.checkNetworkStackPermission; -import static android.net.NetworkStack.checkNetworkStackPermissionOr; - import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.when; -import android.content.Context; import android.os.Build; import android.os.IBinder; @@ -46,44 +34,15 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public class NetworkStackTest { - private static final String [] OTHER_PERMISSION = {"otherpermission1", "otherpermission2"}; - @Rule public DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule(); - @Mock Context mCtx; @Mock private IBinder mConnectorBinder; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); } - @Test - public void testCheckNetworkStackPermission() throws Exception { - when(mCtx.checkCallingOrSelfPermission(eq(NETWORK_STACK))).thenReturn(PERMISSION_GRANTED); - when(mCtx.checkCallingOrSelfPermission(eq(PERMISSION_MAINLINE_NETWORK_STACK))) - .thenReturn(PERMISSION_DENIED); - checkNetworkStackPermission(mCtx); - checkNetworkStackPermissionOr(mCtx, OTHER_PERMISSION); - - when(mCtx.checkCallingOrSelfPermission(eq(NETWORK_STACK))).thenReturn(PERMISSION_DENIED); - when(mCtx.checkCallingOrSelfPermission(eq(PERMISSION_MAINLINE_NETWORK_STACK))) - .thenReturn(PERMISSION_GRANTED); - checkNetworkStackPermission(mCtx); - checkNetworkStackPermissionOr(mCtx, OTHER_PERMISSION); - - when(mCtx.checkCallingOrSelfPermission(any())).thenReturn(PERMISSION_DENIED); - - try { - checkNetworkStackPermissionOr(mCtx, OTHER_PERMISSION); - } catch (SecurityException e) { - // Expect to get a SecurityException - return; - } - - fail("Expect fail but permission granted."); - } - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) public void testGetService() { NetworkStack.setServiceForTest(mConnectorBinder); diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt index 083c8c8741da..9ed55f098a16 100644 --- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt +++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt @@ -38,6 +38,7 @@ import android.net.metrics.IpConnectivityLog import android.os.ConditionVariable import android.os.IBinder import android.os.INetworkManagementService +import android.os.SystemConfigManager import android.os.UserHandle import android.testing.TestableContext import android.util.Log @@ -57,6 +58,7 @@ import org.junit.BeforeClass import org.junit.Test import org.junit.runner.RunWith import org.mockito.AdditionalAnswers +import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.Mockito.any import org.mockito.Mockito.anyInt @@ -94,6 +96,8 @@ class ConnectivityServiceIntegrationTest { private lateinit var netd: INetd @Mock private lateinit var dnsResolver: IDnsResolver + @Mock + private lateinit var systemConfigManager: SystemConfigManager @Spy private var context = TestableContext(realContext) @@ -151,6 +155,11 @@ class ConnectivityServiceIntegrationTest { doReturn(UserHandle.ALL).`when`(asUserCtx).user doReturn(asUserCtx).`when`(context).createContextAsUser(eq(UserHandle.ALL), anyInt()) doNothing().`when`(context).sendStickyBroadcast(any(), any()) + doReturn(Context.SYSTEM_CONFIG_SERVICE).`when`(context) + .getSystemServiceName(SystemConfigManager::class.java) + doReturn(systemConfigManager).`when`(context) + .getSystemService(Context.SYSTEM_CONFIG_SERVICE) + doReturn(IntArray(0)).`when`(systemConfigManager).getSystemPermissionUids(anyString()) networkStackClient = TestNetworkStackClient(realContext) networkStackClient.init() diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 24e559225027..6de1075d519b 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -53,6 +53,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE; import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND; import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA; import static android.net.NetworkCapabilities.NET_CAPABILITY_IA; @@ -238,6 +239,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.SystemClock; +import android.os.SystemConfigManager; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -430,6 +432,7 @@ public class ConnectivityServiceTest { @Mock EthernetManager mEthernetManager; @Mock NetworkPolicyManager mNetworkPolicyManager; @Mock KeyStore mKeyStore; + @Mock SystemConfigManager mSystemConfigManager; private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor = ArgumentCaptor.forClass(ResolverParamsParcel.class); @@ -526,6 +529,7 @@ public class ConnectivityServiceTest { if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager; if (Context.ETHERNET_SERVICE.equals(name)) return mEthernetManager; if (Context.NETWORK_POLICY_SERVICE.equals(name)) return mNetworkPolicyManager; + if (Context.SYSTEM_CONFIG_SERVICE.equals(name)) return mSystemConfigManager; return super.getSystemService(name); } @@ -1432,6 +1436,7 @@ public class ConnectivityServiceTest { applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q; when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any())) .thenReturn(applicationInfo); + when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]); // InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not. // http://b/25897652 . @@ -2783,7 +2788,8 @@ public class ConnectivityServiceTest { if (capability == NET_CAPABILITY_CBS || capability == NET_CAPABILITY_DUN || capability == NET_CAPABILITY_EIMS || capability == NET_CAPABILITY_FOTA || capability == NET_CAPABILITY_IA || capability == NET_CAPABILITY_IMS || - capability == NET_CAPABILITY_RCS || capability == NET_CAPABILITY_XCAP) { + capability == NET_CAPABILITY_RCS || capability == NET_CAPABILITY_XCAP + || capability == NET_CAPABILITY_ENTERPRISE) { assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); } else { assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); @@ -2882,6 +2888,7 @@ public class ConnectivityServiceTest { tryNetworkFactoryRequests(NET_CAPABILITY_IA); tryNetworkFactoryRequests(NET_CAPABILITY_RCS); tryNetworkFactoryRequests(NET_CAPABILITY_XCAP); + tryNetworkFactoryRequests(NET_CAPABILITY_ENTERPRISE); tryNetworkFactoryRequests(NET_CAPABILITY_EIMS); tryNetworkFactoryRequests(NET_CAPABILITY_NOT_METERED); tryNetworkFactoryRequests(NET_CAPABILITY_INTERNET); @@ -6847,7 +6854,7 @@ public class ConnectivityServiceTest { callback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.getUids().size() == 2 && caps.getUids().contains(new UidRange(uid, uid)) - && caps.getUids().contains(UidRange.createForUser(RESTRICTED_USER)) + && caps.getUids().contains(createUidRange(RESTRICTED_USER)) && caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_WIFI)); @@ -6857,7 +6864,7 @@ public class ConnectivityServiceTest { callback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.getUids().size() == 2 && caps.getUids().contains(new UidRange(uid, uid)) - && caps.getUids().contains(UidRange.createForUser(RESTRICTED_USER)) + && caps.getUids().contains(createUidRange(RESTRICTED_USER)) && caps.hasTransport(TRANSPORT_VPN) && !caps.hasTransport(TRANSPORT_WIFI)); @@ -7491,7 +7498,7 @@ public class ConnectivityServiceTest { assertNotNull(underlying); mMockVpn.setVpnType(VpnManager.TYPE_VPN_LEGACY); // The legacy lockdown VPN only supports userId 0. - final Set<UidRange> ranges = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); + final Set<UidRange> ranges = Collections.singleton(createUidRange(PRIMARY_USER)); mMockVpn.registerAgent(ranges); mMockVpn.setUnderlyingNetworks(new Network[]{underlying}); mMockVpn.connect(true); @@ -8410,7 +8417,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); // The uid range needs to cover the test app so the network is visible to it. - final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); + final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER)); mMockVpn.establish(lp, VPN_UID, vpnRange); assertVpnUidRangesUpdated(true, vpnRange, VPN_UID); @@ -8438,7 +8445,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. - final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); + final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER)); mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange); assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID); @@ -8454,7 +8461,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun0")); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); // The uid range needs to cover the test app so the network is visible to it. - final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); + final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER)); mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange); assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID); @@ -8469,7 +8476,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. - final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); + final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER)); mMockVpn.establish(lp, VPN_UID, vpnRange); assertVpnUidRangesUpdated(true, vpnRange, VPN_UID); @@ -8521,7 +8528,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE)); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. - final UidRange vpnRange = UidRange.createForUser(PRIMARY_USER); + final UidRange vpnRange = createUidRange(PRIMARY_USER); final Set<UidRange> vpnRanges = Collections.singleton(vpnRange); mMockVpn.establish(lp, VPN_UID, vpnRanges); assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID); @@ -8720,7 +8727,7 @@ public class ConnectivityServiceTest { private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType) throws Exception { - final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); + final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER)); mMockVpn.setVpnType(vpnType); mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange); assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid); @@ -9279,7 +9286,7 @@ public class ConnectivityServiceTest { lp.setInterfaceName("tun0"); lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); - final UidRange vpnRange = UidRange.createForUser(PRIMARY_USER); + final UidRange vpnRange = createUidRange(PRIMARY_USER); Set<UidRange> vpnRanges = Collections.singleton(vpnRange); mMockVpn.establish(lp, VPN_UID, vpnRanges); assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID); @@ -10338,4 +10345,8 @@ public class ConnectivityServiceTest { mEthernetNetworkAgent.getNetwork().netId, 0 /* times */, true /* shouldDestroyNetwork */); } + + private UidRange createUidRange(int userId) { + return UidRange.createForUser(UserHandle.of(userId)); + } } diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java index c86224a71978..32c95f149979 100644 --- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java +++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java @@ -16,12 +16,16 @@ package com.android.server; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.net.INetd.IF_STATE_DOWN; +import static android.net.INetd.IF_STATE_UP; import static android.system.OsConstants.AF_INET; import static android.system.OsConstants.AF_INET6; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; @@ -36,6 +40,7 @@ import android.content.pm.PackageManager; import android.net.ConnectivityManager; import android.net.INetd; import android.net.InetAddresses; +import android.net.InterfaceConfigurationParcel; import android.net.IpSecAlgorithm; import android.net.IpSecConfig; import android.net.IpSecManager; @@ -48,7 +53,6 @@ import android.net.LinkAddress; import android.net.LinkProperties; import android.net.Network; import android.os.Binder; -import android.os.INetworkManagementService; import android.os.ParcelFileDescriptor; import android.system.Os; import android.test.mock.MockContext; @@ -148,10 +152,17 @@ public class IpSecServiceParameterizedTest { } throw new SecurityException("Unavailable permission requested"); } + + @Override + public int checkCallingOrSelfPermission(String permission) { + if (android.Manifest.permission.NETWORK_STACK.equals(permission)) { + return PERMISSION_GRANTED; + } + throw new UnsupportedOperationException(); + } }; INetd mMockNetd; - INetworkManagementService mNetworkManager; PackageManager mMockPkgMgr; IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig; IpSecService mIpSecService; @@ -181,10 +192,9 @@ public class IpSecServiceParameterizedTest { @Before public void setUp() throws Exception { mMockNetd = mock(INetd.class); - mNetworkManager = mock(INetworkManagementService.class); mMockPkgMgr = mock(PackageManager.class); mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class); - mIpSecService = new IpSecService(mMockContext, mNetworkManager, mMockIpSecSrvConfig); + mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig); // Injecting mock netd when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd); @@ -644,7 +654,10 @@ public class IpSecServiceParameterizedTest { } private IpSecTunnelInterfaceResponse createAndValidateTunnel( - String localAddr, String remoteAddr, String pkgName) { + String localAddr, String remoteAddr, String pkgName) throws Exception { + final InterfaceConfigurationParcel config = new InterfaceConfigurationParcel(); + config.flags = new String[] {IF_STATE_DOWN}; + when(mMockNetd.interfaceGetCfg(anyString())).thenReturn(config); IpSecTunnelInterfaceResponse createTunnelResp = mIpSecService.createTunnelInterface( mSourceAddr, mDestinationAddr, fakeNetwork, new Binder(), pkgName); @@ -674,7 +687,8 @@ public class IpSecServiceParameterizedTest { anyInt(), anyInt(), anyInt()); - verify(mNetworkManager).setInterfaceUp(createTunnelResp.interfaceName); + verify(mMockNetd).interfaceSetCfg(argThat( + config -> Arrays.asList(config.flags).contains(IF_STATE_UP))); } @Test diff --git a/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java b/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java index 788e4efe097e..22a2c94fc194 100644 --- a/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java +++ b/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java @@ -31,7 +31,6 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.os.Binder; import android.os.IBinder; -import android.os.INetworkManagementService; import android.os.RemoteException; import androidx.test.filters.SmallTest; @@ -62,8 +61,7 @@ public class IpSecServiceRefcountedResourceTest { public void setUp() throws Exception { mMockContext = mock(Context.class); mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class); - mIpSecService = new IpSecService( - mMockContext, mock(INetworkManagementService.class), mMockIpSecSrvConfig); + mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig); } private void assertResourceState( diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java index 536e98327e1f..f97eabf6366d 100644 --- a/tests/net/java/com/android/server/IpSecServiceTest.java +++ b/tests/net/java/com/android/server/IpSecServiceTest.java @@ -42,7 +42,6 @@ import android.net.IpSecManager; import android.net.IpSecSpiResponse; import android.net.IpSecUdpEncapResponse; import android.os.Binder; -import android.os.INetworkManagementService; import android.os.ParcelFileDescriptor; import android.os.Process; import android.system.ErrnoException; @@ -116,7 +115,6 @@ public class IpSecServiceTest { } Context mMockContext; - INetworkManagementService mMockNetworkManager; INetd mMockNetd; IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig; IpSecService mIpSecService; @@ -124,10 +122,9 @@ public class IpSecServiceTest { @Before public void setUp() throws Exception { mMockContext = mock(Context.class); - mMockNetworkManager = mock(INetworkManagementService.class); mMockNetd = mock(INetd.class); mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class); - mIpSecService = new IpSecService(mMockContext, mMockNetworkManager, mMockIpSecSrvConfig); + mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig); // Injecting mock netd when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd); @@ -135,7 +132,7 @@ public class IpSecServiceTest { @Test public void testIpSecServiceCreate() throws InterruptedException { - IpSecService ipSecSrv = IpSecService.create(mMockContext, mMockNetworkManager); + IpSecService ipSecSrv = IpSecService.create(mMockContext); assertNotNull(ipSecSrv); } @@ -608,7 +605,7 @@ public class IpSecServiceTest { public void testOpenUdpEncapSocketTagsSocket() throws Exception { IpSecService.UidFdTagger mockTagger = mock(IpSecService.UidFdTagger.class); IpSecService testIpSecService = new IpSecService( - mMockContext, mMockNetworkManager, mMockIpSecSrvConfig, mockTagger); + mMockContext, mMockIpSecSrvConfig, mockTagger); IpSecUdpEncapResponse udpEncapResp = testIpSecService.openUdpEncapsulationSocket(0, new Binder()); diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java index 8f5ae97bc4c5..e4e24b464838 100644 --- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java @@ -61,6 +61,7 @@ import android.content.pm.PackageManagerInternal; import android.net.INetd; import android.net.UidRange; import android.os.Build; +import android.os.SystemConfigManager; import android.os.UserHandle; import android.os.UserManager; import android.util.SparseIntArray; @@ -114,6 +115,7 @@ public class PermissionMonitorTest { @Mock private PackageManagerInternal mMockPmi; @Mock private UserManager mUserManager; @Mock private PermissionMonitor.Dependencies mDeps; + @Mock private SystemConfigManager mSystemConfigManager; private PermissionMonitor mPermissionMonitor; @@ -124,6 +126,11 @@ public class PermissionMonitorTest { when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager); when(mUserManager.getUserHandles(eq(true))).thenReturn( Arrays.asList(new UserHandle[] { MOCK_USER1, MOCK_USER2 })); + when(mContext.getSystemServiceName(SystemConfigManager.class)) + .thenReturn(Context.SYSTEM_CONFIG_SERVICE); + when(mContext.getSystemService(Context.SYSTEM_CONFIG_SERVICE)) + .thenReturn(mSystemConfigManager); + when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]); mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps)); @@ -747,4 +754,20 @@ public class PermissionMonitorTest { GET_PERMISSIONS | MATCH_ANY_USER); assertTrue(monitor.hasPermission(systemInfo, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); } + + @Test + public void testUpdateUidPermissionsFromSystemConfig() throws Exception { + final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); + when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(new ArrayList<>()); + when(mSystemConfigManager.getSystemPermissionUids(eq(INTERNET))) + .thenReturn(new int[]{ MOCK_UID1, MOCK_UID2 }); + when(mSystemConfigManager.getSystemPermissionUids(eq(UPDATE_DEVICE_STATS))) + .thenReturn(new int[]{ MOCK_UID2 }); + + mPermissionMonitor.startMonitoring(); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{ MOCK_UID1 }); + mNetdServiceMonitor.expectPermission( + INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS, + new int[]{ MOCK_UID2 }); + } } diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index cffd2d1d428f..7489a0f889dc 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -21,6 +21,8 @@ import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; import static android.content.pm.UserInfo.FLAG_PRIMARY; import static android.content.pm.UserInfo.FLAG_RESTRICTED; import static android.net.ConnectivityManager.NetworkCallback; +import static android.net.INetd.IF_STATE_DOWN; +import static android.net.INetd.IF_STATE_UP; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -62,6 +64,7 @@ import android.net.ConnectivityManager; import android.net.INetd; import android.net.Ikev2VpnProfile; import android.net.InetAddresses; +import android.net.InterfaceConfigurationParcel; import android.net.IpPrefix; import android.net.IpSecManager; import android.net.IpSecTunnelInterfaceResponse; @@ -179,7 +182,8 @@ public class VpnTest { mPackages.put(PKGS[i], PKG_UIDS[i]); } } - private static final UidRange PRI_USER_RANGE = UidRange.createForUser(primaryUser.id); + private static final UidRange PRI_USER_RANGE = + UidRange.createForUser(UserHandle.of(primaryUser.id)); @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Context mContext; @Mock private UserManager mUserManager; @@ -269,7 +273,7 @@ public class VpnTest { vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, null, null); assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] { - PRI_USER_RANGE, UidRange.createForUser(restrictedProfileA.id) + PRI_USER_RANGE, UidRange.createForUser(UserHandle.of(restrictedProfileA.id)) })), ranges); } @@ -872,17 +876,28 @@ public class VpnTest { eq(AppOpsManager.MODE_IGNORED)); } - private NetworkCallback triggerOnAvailableAndGetCallback() { + private NetworkCallback triggerOnAvailableAndGetCallback() throws Exception { final ArgumentCaptor<NetworkCallback> networkCallbackCaptor = ArgumentCaptor.forClass(NetworkCallback.class); verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)) .requestNetwork(any(), networkCallbackCaptor.capture()); + // onAvailable() will trigger onDefaultNetworkChanged(), so NetdUtils#setInterfaceUp will be + // invoked. Set the return value of INetd#interfaceGetCfg to prevent NullPointerException. + final InterfaceConfigurationParcel config = new InterfaceConfigurationParcel(); + config.flags = new String[] {IF_STATE_DOWN}; + when(mNetd.interfaceGetCfg(anyString())).thenReturn(config); final NetworkCallback cb = networkCallbackCaptor.getValue(); cb.onAvailable(TEST_NETWORK); return cb; } + private void verifyInterfaceSetCfgWithFlags(String flag) throws Exception { + // Add a timeout for waiting for interfaceSetCfg to be called. + verify(mNetd, timeout(TEST_TIMEOUT_MS)).interfaceSetCfg(argThat( + config -> Arrays.asList(config.flags).contains(flag))); + } + @Test public void testStartPlatformVpnAuthenticationFailed() throws Exception { final ArgumentCaptor<IkeSessionCallback> captor = @@ -894,6 +909,8 @@ public class VpnTest { final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), (mVpnProfile)); final NetworkCallback cb = triggerOnAvailableAndGetCallback(); + verifyInterfaceSetCfgWithFlags(IF_STATE_UP); + // Wait for createIkeSession() to be called before proceeding in order to ensure consistent // state verify(mIkev2SessionCreator, timeout(TEST_TIMEOUT_MS)) @@ -912,6 +929,8 @@ public class VpnTest { final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), mVpnProfile); final NetworkCallback cb = triggerOnAvailableAndGetCallback(); + verifyInterfaceSetCfgWithFlags(IF_STATE_UP); + // Wait for createIkeSession() to be called before proceeding in order to ensure consistent // state verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb)); diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java index 708767605508..66590c92579b 100644 --- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java +++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java @@ -16,6 +16,8 @@ package android.net.vcn; +import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE; + import static androidx.test.InstrumentationRegistry.getContext; import static org.junit.Assert.assertEquals; @@ -204,10 +206,13 @@ public class VcnManagerTest { cbBinder.onEnteredSafeMode(); verify(mMockStatusCallback).onEnteredSafeMode(); + cbBinder.onVcnStatusChanged(VCN_STATUS_CODE_ACTIVE); + verify(mMockStatusCallback).onVcnStatusChanged(VCN_STATUS_CODE_ACTIVE); + cbBinder.onGatewayConnectionError( UNDERLYING_NETWORK_CAPABILITIES, VcnManager.VCN_ERROR_CODE_NETWORK_ERROR, - "java.net.UnknownHostException", + UnknownHostException.class.getName(), "exception_message"); verify(mMockStatusCallback) .onGatewayConnectionError( diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java index 3ba0a1f53a9f..a674425efea3 100644 --- a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java +++ b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java @@ -46,6 +46,6 @@ public class VcnUnderlyingNetworkPolicyTest { @Test public void testParcelUnparcel() { - assertParcelSane(SAMPLE_NETWORK_POLICY, 2); + assertParcelSane(SAMPLE_NETWORK_POLICY, 1); } } diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index 45b2381ce06d..9b500a7271d7 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -43,7 +43,6 @@ import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -59,6 +58,7 @@ import android.net.vcn.IVcnStatusCallback; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; import android.net.vcn.VcnConfigTest; +import android.net.vcn.VcnManager; import android.net.vcn.VcnUnderlyingNetworkPolicy; import android.net.wifi.WifiInfo; import android.os.IBinder; @@ -783,7 +783,7 @@ public class VcnManagementServiceTest { true /* hasPermissionsforSubGroup */, true /* hasLocationPermission */); - verify(mMockStatusCallback, times(1)).onEnteredSafeMode(); + verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_SAFE_MODE); } @Test @@ -795,7 +795,8 @@ public class VcnManagementServiceTest { false /* hasPermissionsforSubGroup */, true /* hasLocationPermission */); - verify(mMockStatusCallback, never()).onEnteredSafeMode(); + verify(mMockStatusCallback, never()) + .onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_SAFE_MODE); } @Test @@ -807,7 +808,8 @@ public class VcnManagementServiceTest { true /* hasPermissionsforSubGroup */, false /* hasLocationPermission */); - verify(mMockStatusCallback, never()).onEnteredSafeMode(); + verify(mMockStatusCallback, never()) + .onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_SAFE_MODE); } @Test diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py index f0b759547a93..ebc0ec1640f5 100755 --- a/tools/fonts/fontchain_linter.py +++ b/tools/fonts/fontchain_linter.py @@ -11,6 +11,12 @@ from fontTools import ttLib EMOJI_VS = 0xFE0F +#TODO(179952916): Rename CutiveMono and DancingScript +CANONICAL_NAME_EXCEPTION_LIST = [ + 'CutiveMono.ttf', + 'DancingScript-Regular.ttf', +] + LANG_TO_SCRIPT = { 'as': 'Beng', 'be': 'Cyrl', @@ -665,6 +671,53 @@ def check_cjk_punctuation(): assert_font_supports_none_of_chars(record.font, cjk_punctuation, name) +def getPostScriptName(font): + ttf = open_font(font) + nameTable = ttf['name'] + for name in nameTable.names: + if name.nameID == 6 and name.platformID == 3 and name.platEncID == 1 and name.langID == 0x0409: + return str(name) + + +def getSuffix(font): + file_path, index = font + with open(path.join(_fonts_dir, file_path), 'rb') as f: + tag = f.read(4) + isCollection = tag == b'ttcf' + + ttf = open_font(font) + isType1 = ('CFF ' in ttf or 'CFF2' in ttf) + + if isType1: + if isCollection: + return '.otc' + else: + return '.otf' + else: + if isCollection: + return '.ttc' + else: + return '.ttf' + + +def check_canonical_name(): + for record in _all_fonts: + file_name, index = record.font + if file_name in CANONICAL_NAME_EXCEPTION_LIST: + continue + + if index and index != 0: + continue + + psName = getPostScriptName(record.font) + assert psName, 'PostScript must be defined' + + suffix = getSuffix(record.font) + canonicalName = '%s%s' % (psName, suffix) + + assert file_name == canonicalName, ( + '%s is not a canonical name. Must be %s' % (file_name, canonicalName)) + def main(): global _fonts_dir target_out = sys.argv[1] @@ -682,6 +735,8 @@ def main(): check_cjk_punctuation() + check_canonical_name() + check_emoji = sys.argv[2] if check_emoji == 'true': ucd_path = sys.argv[3] diff --git a/tools/powerstats/Android.bp b/tools/powerstats/Android.bp index af41144167a9..9c58daf0f922 100644 --- a/tools/powerstats/Android.bp +++ b/tools/powerstats/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + java_binary_host { name: "PowerStatsServiceProtoParser", manifest: "PowerStatsServiceProtoParser_manifest.txt", diff --git a/tools/processors/intdef_mappings/Android.bp b/tools/processors/intdef_mappings/Android.bp index e255f7c784d3..82a5dac21160 100644 --- a/tools/processors/intdef_mappings/Android.bp +++ b/tools/processors/intdef_mappings/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + java_plugin { name: "intdef-annotation-processor", @@ -30,4 +39,4 @@ java_test_host { ], test_suites: ["general-tests"], -}
\ No newline at end of file +} diff --git a/tools/xmlpersistence/Android.bp b/tools/xmlpersistence/Android.bp index d58d0dcdc45a..0b6dba626794 100644 --- a/tools/xmlpersistence/Android.bp +++ b/tools/xmlpersistence/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + java_binary_host { name: "xmlpersistence_cli", manifest: "manifest.txt", |