diff options
495 files changed, 14556 insertions, 8358 deletions
diff --git a/Android.bp b/Android.bp index e4c5c37ab576..908280e5d7f3 100644 --- a/Android.bp +++ b/Android.bp @@ -371,7 +371,6 @@ filegroup { srcs: [ // Java/AIDL sources under frameworks/base ":framework-blobstore-sources", - ":framework-connectivity-sources", // framework-connectivity is not yet a module ":framework-core-sources", ":framework-drm-sources", ":framework-graphics-nonupdatable-sources", @@ -437,6 +436,7 @@ filegroup { name: "framework-updatable-sources", srcs: [ ":framework-appsearch-sources", + ":framework-connectivity-sources", ":framework-graphics-srcs", ":framework-mediaprovider-sources", ":framework-permission-sources", @@ -639,6 +639,7 @@ java_defaults { defaults: ["framework-aidl-export-defaults"], srcs: [ ":framework-non-updatable-sources", + ":framework-connectivity-sources", "core/java/**/*.logtags", ], // See comment on framework-atb-backward-compatibility module below @@ -700,6 +701,8 @@ java_library { apex_available: ["//apex_available:platform"], visibility: [ "//frameworks/base", + // TODO: remove when framework-connectivity can build against API + "//frameworks/base/packages/Connectivity/framework", // TODO(b/147128803) remove the below lines "//frameworks/base/apex/appsearch/framework", "//frameworks/base/apex/blobstore/framework", @@ -1452,6 +1455,7 @@ java_library { ], libs: [ "framework-annotations-lib", + "framework-connectivity", "unsupportedappusage", ], visibility: [ diff --git a/StubLibraries.bp b/StubLibraries.bp index 4bd524f229ca..bff222e7d42f 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -58,6 +58,9 @@ stubs_defaults { local_include_dirs: [ "apex/media/aidl/stable", "media/aidl", + // TODO: move to include-dirs for packages/modules/Connectivity when this moves out of + // frameworks/base + "packages/Connectivity/framework/aidl-export", "telephony/java", ], include_dirs: ["frameworks/av/aidl"], @@ -310,6 +313,7 @@ java_library_static { "art.module.public.api.stubs", "conscrypt.module.public.api.stubs", "framework-appsearch.stubs", + "framework-connectivity.stubs", "framework-graphics.stubs", "framework-media.stubs", "framework-mediaprovider.stubs", @@ -334,6 +338,7 @@ java_library_static { "art.module.public.api.stubs", "conscrypt.module.public.api.stubs", "framework-appsearch.stubs.system", + "framework-connectivity.stubs.system", "framework-graphics.stubs.system", "framework-media.stubs.system", "framework-mediaprovider.stubs.system", @@ -374,6 +379,7 @@ java_library_static { "art.module.public.api.stubs", "conscrypt.module.public.api.stubs", "framework-appsearch.stubs.system", + "framework-connectivity.stubs.system", "framework-graphics.stubs.system", "framework-media.stubs.system", "framework-mediaprovider.stubs.system", diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt index cc79f6bf9682..168c7c2f13cd 100644 --- a/apex/appsearch/framework/api/current.txt +++ b/apex/appsearch/framework/api/current.txt @@ -148,7 +148,7 @@ package android.app.appsearch { 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 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 @NonNull public android.app.appsearch.SearchResults search(@NonNull String, @NonNull android.app.appsearch.SearchSpec); 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>>); } @@ -217,7 +217,7 @@ package android.app.appsearch { public class GlobalSearchSession implements java.io.Closeable { method public void close(); - method @NonNull public android.app.appsearch.SearchResults search(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor); + method @NonNull public android.app.appsearch.SearchResults search(@NonNull String, @NonNull android.app.appsearch.SearchSpec); } public class PackageIdentifier { @@ -287,7 +287,7 @@ package android.app.appsearch { public class SearchResults implements java.io.Closeable { method public void close(); - method public void getNextPage(@NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.util.List<android.app.appsearch.SearchResult>>>); + method public void getNextPage(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.util.List<android.app.appsearch.SearchResult>>>); } public final class SearchSpec { diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java index 519c14f7bb40..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,7 +108,8 @@ 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}. */ @@ -149,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> { @@ -160,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( @@ -173,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( @@ -188,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( @@ -209,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/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java index 3c02d108507c..24cc60e5eef5 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 @@ -463,21 +395,15 @@ public final class AppSearchSession implements Closeable { * @param queryExpression query string to search. * @param searchSpec spec for setting document filters, adding projection, setting term match * type, etc. - * @param executor Executor on which to invoke the callback of the following request - * {@link SearchResults#getNextPage}. * @return a {@link SearchResults} object for retrieved matched documents. */ @NonNull - public SearchResults search( - @NonNull String queryExpression, - @NonNull SearchSpec searchSpec, - @NonNull @CallbackExecutor Executor executor) { + public SearchResults search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec) { Objects.requireNonNull(queryExpression); Objects.requireNonNull(searchSpec); - Objects.requireNonNull(executor); Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); return new SearchResults(mService, mPackageName, mDatabaseName, queryExpression, - searchSpec, mUserId, executor); + searchSpec, mUserId); } /** diff --git a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java index 09bca4fb3b9d..8dd9dc1be312 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}. @@ -107,21 +106,15 @@ public class GlobalSearchSession implements Closeable { * @param queryExpression query string to search. * @param searchSpec spec for setting document filters, adding projection, setting term match * type, etc. - * @param executor Executor on which to invoke the callback of the following request - * {@link SearchResults#getNextPage}. * @return a {@link SearchResults} object for retrieved matched documents. */ @NonNull - public SearchResults search( - @NonNull String queryExpression, - @NonNull SearchSpec searchSpec, - @NonNull @CallbackExecutor Executor executor) { + public SearchResults search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec) { Objects.requireNonNull(queryExpression); Objects.requireNonNull(searchSpec); - Objects.requireNonNull(executor); Preconditions.checkState(!mIsClosed, "GlobalSearchSession has already been closed"); return new SearchResults(mService, mPackageName, /*databaseName=*/null, queryExpression, - searchSpec, mUserId, executor); + searchSpec, mUserId); } /** Closes the {@link GlobalSearchSession}. */ diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java index a63e01555f6c..531c98425288 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. */ @@ -61,8 +65,6 @@ public class SearchResults implements Closeable { @UserIdInt private final int mUserId; - private final Executor mExecutor; - private long mNextPageToken; private boolean mIsFirstLoad = true; @@ -75,28 +77,31 @@ public class SearchResults implements Closeable { @Nullable String databaseName, @NonNull String queryExpression, @NonNull SearchSpec searchSpec, - @UserIdInt int userId, - @NonNull @CallbackExecutor Executor executor) { + @UserIdInt int userId) { mService = Objects.requireNonNull(service); mPackageName = packageName; mDatabaseName = databaseName; mQueryExpression = Objects.requireNonNull(queryExpression); mSearchSpec = Objects.requireNonNull(searchSpec); mUserId = userId; - mExecutor = Objects.requireNonNull(executor); } /** - * 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 executor Executor on which to invoke the callback. * @param callback Callback to receive the pending result of performing this operation. */ - public void getNextPage(@NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) { + public void getNextPage( + @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) { + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); Preconditions.checkState(!mIsClosed, "SearchResults has already been closed"); try { if (mIsFirstLoad) { @@ -104,14 +109,14 @@ public class SearchResults implements Closeable { if (mDatabaseName == null) { // Global query, there's no one package-database combination to check. mService.globalQuery(mPackageName, mQueryExpression, - mSearchSpec.getBundle(), mUserId, wrapCallback(callback)); + mSearchSpec.getBundle(), mUserId, wrapCallback(executor, callback)); } else { // Normal local query, pass in specified database. mService.query(mPackageName, mDatabaseName, mQueryExpression, - mSearchSpec.getBundle(), mUserId, wrapCallback(callback)); + mSearchSpec.getBundle(), mUserId, wrapCallback(executor, callback)); } } else { - mService.getNextPage(mNextPageToken, mUserId, wrapCallback(callback)); + mService.getNextPage(mNextPageToken, mUserId, wrapCallback(executor, callback)); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -131,10 +136,11 @@ public class SearchResults implements Closeable { } private IAppSearchResultCallback wrapCallback( + @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) { return new IAppSearchResultCallback.Stub() { public void onResult(AppSearchResult result) { - mExecutor.execute(() -> invokeCallback(result, callback)); + executor.execute(() -> invokeCallback(result, callback)); } }; } 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 57700f89403e..01473be062bc 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java @@ -30,8 +30,6 @@ import java.util.List; /** * Encapsulates a request to index documents into an {@link AppSearchSession} database. * - * <p>@see AppSearchSession#putDocuments - * * @see AppSearchSession#put */ public final class PutDocumentsRequest { 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/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/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt index fc0299ef88af..2c9477a5d76b 100644 --- a/apex/appsearch/synced_jetpack_changeid.txt +++ b/apex/appsearch/synced_jetpack_changeid.txt @@ -1 +1 @@ -I895f5fb3bcb4be0642c6193000e57d80aafe2166 +I593dfd22279739e5f578e07d36a55cf02ee942c5 diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java index afa633a48b2b..9ef6e0b2dee8 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java @@ -124,8 +124,7 @@ public class AppSearchSessionShimImpl implements AppSearchSessionShim { @NonNull public SearchResultsShim search( @NonNull String queryExpression, @NonNull SearchSpec searchSpec) { - SearchResults searchResults = - mAppSearchSession.search(queryExpression, searchSpec, mExecutor); + SearchResults searchResults = mAppSearchSession.search(queryExpression, searchSpec); return new SearchResultsShimImpl(searchResults, mExecutor); } diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java index 6595d8d4abba..69a4c18c4028 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java @@ -75,8 +75,7 @@ public class GlobalSearchSessionShimImpl implements GlobalSearchSessionShim { @Override public SearchResultsShim search( @NonNull String queryExpression, @NonNull SearchSpec searchSpec) { - SearchResults searchResults = - mGlobalSearchSession.search(queryExpression, searchSpec, mExecutor); + SearchResults searchResults = mGlobalSearchSession.search(queryExpression, searchSpec); return new SearchResultsShimImpl(searchResults, mExecutor); } diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/SearchResultsShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/SearchResultsShimImpl.java index 75add81c8d64..5f26e8cba585 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/SearchResultsShimImpl.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/SearchResultsShimImpl.java @@ -47,7 +47,7 @@ public class SearchResultsShimImpl implements SearchResultsShim { @NonNull public ListenableFuture<List<SearchResult>> getNextPage() { SettableFuture<AppSearchResult<List<SearchResult>>> future = SettableFuture.create(); - mSearchResults.getNextPage(future::set); + mSearchResults.getNextPage(mExecutor, future::set); return Futures.transform(future, AppSearchResult::getResultValue, mExecutor); } diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java index 34c7ccbd4df1..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,90 +33,16 @@ 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 {@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. - * - * <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. 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/framework/java/android/os/IDeviceIdleController.aidl b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl index 5693abe4d4e1..43d4873a3540 100644 --- a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl +++ b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl @@ -43,10 +43,10 @@ interface IDeviceIdleController { boolean isPowerSaveWhitelistApp(String name); @UnsupportedAppUsage(maxTargetSdk = 30, publicAlternatives = "Use SystemApi {@code PowerWhitelistManager#whitelistAppTemporarily(String, int, String)}.") - void addPowerSaveTempWhitelistApp(String name, long duration, int userId, String reason); - long addPowerSaveTempWhitelistAppForMms(String name, int userId, String reason); - long addPowerSaveTempWhitelistAppForSms(String name, int userId, String reason); - long whitelistAppTemporarily(String name, int userId, String reason); + void addPowerSaveTempWhitelistApp(String name, long duration, int userId, int reasonCode, String reason); + long addPowerSaveTempWhitelistAppForMms(String name, int userId, int reasonCode, String reason); + long addPowerSaveTempWhitelistAppForSms(String name, int userId, int reasonCode, String reason); + long whitelistAppTemporarily(String name, int userId, int reasonCode, String reason); void exitIdle(String reason); int setPreIdleTimeoutMode(int Mode); void resetPreIdleTimeoutMode(); diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java index df0e157abc6a..df690d00a322 100644 --- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java +++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java @@ -16,8 +16,16 @@ package android.os; +import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; +import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP; +import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; +import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT; +import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI; +import static android.app.ActivityManager.PROCESS_STATE_TOP; + import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; @@ -94,6 +102,239 @@ public class PowerWhitelistManager { @Retention(RetentionPolicy.SOURCE) public @interface TempAllowListType {} + /* Reason code for BG-FGS-launch. */ + /** + * BG-FGS-launch is denied. + * @hide + */ + public static final int REASON_DENIED = -1; + /** + * The default reason code if reason is unknown. + */ + public static final int REASON_UNKNOWN = 0; + /** + * Use REASON_OTHER if there is no better choice. + */ + public static final int REASON_OTHER = 1; + /** @hide */ + public static final int REASON_PROC_STATE_PERSISTENT = 10; + /** @hide */ + public static final int REASON_PROC_STATE_PERSISTENT_UI = 11; + /** @hide */ + public static final int REASON_PROC_STATE_TOP = 12; + /** @hide */ + public static final int REASON_PROC_STATE_BTOP = 13; + /** @hide */ + public static final int REASON_PROC_STATE_FGS = 14; + /** @hide */ + public static final int REASON_PROC_STATE_BFGS = 15; + /** @hide */ + public static final int REASON_UID_VISIBLE = 50; + /** @hide */ + public static final int REASON_SYSTEM_UID = 51; + /** @hide */ + public static final int REASON_ACTIVITY_STARTER = 52; + /** @hide */ + public static final int REASON_START_ACTIVITY_FLAG = 53; + /** @hide */ + public static final int REASON_FGS_BINDING = 54; + /** @hide */ + public static final int REASON_DEVICE_OWNER = 55; + /** @hide */ + public static final int REASON_PROFILE_OWNER = 56; + /** @hide */ + public static final int REASON_COMPANION_DEVICE_MANAGER = 57; + /** + * START_ACTIVITIES_FROM_BACKGROUND permission. + * @hide + */ + public static final int REASON_BACKGROUND_ACTIVITY_PERMISSION = 58; + /** + * START_FOREGROUND_SERVICES_FROM_BACKGROUND permission. + * @hide + */ + public static final int REASON_BACKGROUND_FGS_PERMISSION = 59; + /** @hide */ + public static final int REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION = 60; + /** @hide */ + public static final int REASON_INSTR_BACKGROUND_FGS_PERMISSION = 61; + /** @hide */ + public static final int REASON_SYSTEM_ALERT_WINDOW_PERMISSION = 62; + /** @hide */ + public static final int REASON_DEVICE_DEMO_MODE = 63; + /** @hide */ + public static final int REASON_EXEMPTED_PACKAGE = 64; + /** @hide */ + public static final int REASON_ALLOWLISTED_PACKAGE = 65; + /** + * If it's because of a role, + * @hide + */ + public static final int REASON_APPOP = 66; + + /* BG-FGS-launch is allowed by temp-allowlist or system-allowlist. + Reason code for temp and system allowlist starts here. + */ + public static final int REASON_GEOFENCING = 100; + public static final int REASON_PUSH_MESSAGING = 101; + public static final int REASON_ACTIVITY_RECOGNITION = 102; + + /** + * Broadcast ACTION_BOOT_COMPLETED. + * @hide + */ + public static final int REASON_BOOT_COMPLETED = 103; + /** + * Broadcast ACTION_PRE_BOOT_COMPLETED. + * @hide + */ + public static final int REASON_PRE_BOOT_COMPLETED = 104; + + /** + * Broadcast ACTION_LOCKED_BOOT_COMPLETED. + * @hide + */ + public static final int REASON_LOCKED_BOOT_COMPLETED = 105; + /** + * Device idle system allowlist, including EXCEPT-IDLE + * @hide + */ + public static final int REASON_SYSTEM_ALLOW_LISTED = 106; + /** @hide */ + public static final int REASON_ALARM_MANAGER_ALARM_CLOCK = 107; + /** + * AlarmManagerService. + * @hide + */ + public static final int REASON_ALARM_MANAGER_WHILE_IDLE = 108; + /** + * ActiveServices. + * @hide + */ + public static final int REASON_SERVICE_LAUNCH = 109; + /** + * KeyChainSystemService. + * @hide + */ + public static final int REASON_KEY_CHAIN = 110; + /** + * PackageManagerService. + * @hide + */ + public static final int REASON_PACKAGE_VERIFIER = 111; + /** + * SyncManager. + * @hide + */ + public static final int REASON_SYNC_MANAGER = 112; + /** + * DomainVerificationProxyV1. + * @hide + */ + public static final int REASON_DOMAIN_VERIFICATION_V1 = 113; + /** + * DomainVerificationProxyV2. + * @hide + */ + public static final int REASON_DOMAIN_VERIFICATION_V2 = 114; + /** @hide */ + public static final int REASON_VPN = 115; + /** + * NotificationManagerService. + * @hide + */ + public static final int REASON_NOTIFICATION_SERVICE = 116; + /** + * Broadcast ACTION_MY_PACKAGE_REPLACED. + * @hide + */ + public static final int REASON_PACKAGE_REPLACED = 117; + /** + * LocationProviderManager. + * @hide + */ + public static final int REASON_LOCATION_PROVIDER = 118; + /** + * MediaButtonReceiver. + * @hide + */ + public static final int REASON_MEDIA_BUTTON = 119; + /** + * InboundSmsHandler. + * @hide + */ + public static final int REASON_EVENT_SMS = 120; + /** + * InboundSmsHandler. + * @hide + */ + public static final int REASON_EVENT_MMS = 121; + /** + * Shell app. + * @hide + */ + public static final int REASON_SHELL = 122; + + /** + * The list of BG-FGS-Launch and temp-allowlist reason code. + * @hide + */ + @IntDef(flag = true, prefix = { "REASON_" }, value = { + // BG-FGS-Launch reasons. + REASON_DENIED, + REASON_UNKNOWN, + REASON_OTHER, + REASON_PROC_STATE_PERSISTENT, + REASON_PROC_STATE_PERSISTENT_UI, + REASON_PROC_STATE_TOP, + REASON_PROC_STATE_BTOP, + REASON_PROC_STATE_FGS, + REASON_PROC_STATE_BFGS, + REASON_UID_VISIBLE, + REASON_SYSTEM_UID, + REASON_ACTIVITY_STARTER, + REASON_START_ACTIVITY_FLAG, + REASON_FGS_BINDING, + REASON_DEVICE_OWNER, + REASON_PROFILE_OWNER, + REASON_COMPANION_DEVICE_MANAGER, + REASON_BACKGROUND_ACTIVITY_PERMISSION, + REASON_BACKGROUND_FGS_PERMISSION, + REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION, + REASON_INSTR_BACKGROUND_FGS_PERMISSION, + REASON_SYSTEM_ALERT_WINDOW_PERMISSION, + REASON_DEVICE_DEMO_MODE, + REASON_EXEMPTED_PACKAGE, + REASON_ALLOWLISTED_PACKAGE, + REASON_APPOP, + // temp and system allowlist reasons. + REASON_GEOFENCING, + REASON_PUSH_MESSAGING, + REASON_ACTIVITY_RECOGNITION, + REASON_BOOT_COMPLETED, + REASON_PRE_BOOT_COMPLETED, + REASON_LOCKED_BOOT_COMPLETED, + REASON_SYSTEM_ALLOW_LISTED, + REASON_ALARM_MANAGER_ALARM_CLOCK, + REASON_ALARM_MANAGER_WHILE_IDLE, + REASON_SERVICE_LAUNCH, + REASON_KEY_CHAIN, + REASON_PACKAGE_VERIFIER, + REASON_SYNC_MANAGER, + REASON_DOMAIN_VERIFICATION_V1, + REASON_DOMAIN_VERIFICATION_V2, + REASON_VPN, + REASON_NOTIFICATION_SERVICE, + REASON_PACKAGE_REPLACED, + REASON_LOCATION_PROVIDER, + REASON_MEDIA_BUTTON, + REASON_EVENT_SMS, + REASON_EVENT_MMS, + REASON_SHELL, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ReasonCode {} + /** * @hide */ @@ -184,19 +425,34 @@ public class PowerWhitelistManager { * * @param packageName The package to add to the temp whitelist * @param durationMs How long to keep the app on the temp whitelist for (in milliseconds) + * @param reasonCode one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure. + * @param reason a optional human readable reason string, could be null or empty string. */ @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) - public void whitelistAppTemporarily(@NonNull String packageName, long durationMs) { - String reason = "from:" + UserHandle.formatUid(Binder.getCallingUid()); + public void whitelistAppTemporarily(@NonNull String packageName, long durationMs, + @ReasonCode int reasonCode, @Nullable String reason) { try { mService.addPowerSaveTempWhitelistApp(packageName, durationMs, mContext.getUserId(), - reason); + reasonCode, reason); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** + * Add an app to the temporary whitelist for a short amount of time. + * + * @param packageName The package to add to the temp whitelist + * @param durationMs How long to keep the app on the temp whitelist for (in milliseconds) + * @deprecated Use {@link #whitelistAppTemporarily(String, long, int, String)} instead + */ + @Deprecated + @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) + public void whitelistAppTemporarily(@NonNull String packageName, long durationMs) { + whitelistAppTemporarily(packageName, durationMs, REASON_UNKNOWN, packageName); + } + + /** * Add an app to the temporary whitelist for a short amount of time for a specific reason. The * temporary whitelist is kept separately from the permanent whitelist and apps are * automatically removed from the temporary whitelist after a predetermined amount of time. @@ -204,27 +460,179 @@ public class PowerWhitelistManager { * @param packageName The package to add to the temp whitelist * @param event The reason to add the app to the temp whitelist * @param reason A human-readable reason explaining why the app is temp whitelisted. Only - * used for logging purposes + * used for logging purposes. Could be null or empty string. * @return The duration (in milliseconds) that the app is whitelisted for + * @deprecated Use {@link #whitelistAppTemporarilyForEvent(String, int, int, String)} instead */ + @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String packageName, - @WhitelistEvent int event, @NonNull String reason) { + @WhitelistEvent int event, @Nullable String reason) { + return whitelistAppTemporarilyForEvent(packageName, event, REASON_UNKNOWN, reason); + } + + /** + * Add an app to the temporary whitelist for a short amount of time for a specific reason. The + * temporary whitelist is kept separately from the permanent whitelist and apps are + * automatically removed from the temporary whitelist after a predetermined amount of time. + * + * @param packageName The package to add to the temp whitelist + * @param event The reason to add the app to the temp whitelist + * @param reasonCode one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure. + * @param reason A human-readable reason explaining why the app is temp whitelisted. Only + * used for logging purposes. Could be null or empty string. + * @return The duration (in milliseconds) that the app is whitelisted for + */ + @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) + public long whitelistAppTemporarilyForEvent(@NonNull String packageName, + @WhitelistEvent int event, @ReasonCode int reasonCode, @Nullable String reason) { try { switch (event) { case EVENT_MMS: return mService.addPowerSaveTempWhitelistAppForMms( - packageName, mContext.getUserId(), reason); + packageName, mContext.getUserId(), reasonCode, reason); case EVENT_SMS: return mService.addPowerSaveTempWhitelistAppForSms( - packageName, mContext.getUserId(), reason); + packageName, mContext.getUserId(), reasonCode, reason); case EVENT_UNSPECIFIED: default: return mService.whitelistAppTemporarily( - packageName, mContext.getUserId(), reason); + packageName, mContext.getUserId(), reasonCode, reason); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } + + /** + * @hide + */ + public static @ReasonCode int getReasonCodeFromProcState(int procState) { + if (procState <= PROCESS_STATE_PERSISTENT) { + return REASON_PROC_STATE_PERSISTENT; + } else if (procState <= PROCESS_STATE_PERSISTENT_UI) { + return REASON_PROC_STATE_PERSISTENT_UI; + } else if (procState <= PROCESS_STATE_TOP) { + return REASON_PROC_STATE_TOP; + } else if (procState <= PROCESS_STATE_BOUND_TOP) { + return REASON_PROC_STATE_BTOP; + } else if (procState <= PROCESS_STATE_FOREGROUND_SERVICE) { + return REASON_PROC_STATE_FGS; + } else if (procState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { + return REASON_PROC_STATE_BFGS; + } else { + return REASON_DENIED; + } + } + + /** + * Return string name of the integer reason code. + * @hide + * @param reasonCode + * @return string name of the reason code. + */ + public static String reasonCodeToString(@ReasonCode int reasonCode) { + switch (reasonCode) { + case REASON_DENIED: + return "DENIED"; + case REASON_UNKNOWN: + return "UNKNOWN"; + case REASON_OTHER: + return "OTHER"; + case REASON_PROC_STATE_PERSISTENT: + return "PROC_STATE_PERSISTENT"; + case REASON_PROC_STATE_PERSISTENT_UI: + return "PROC_STATE_PERSISTENT_UI"; + case REASON_PROC_STATE_TOP: + return "PROC_STATE_TOP"; + case REASON_PROC_STATE_BTOP: + return "PROC_STATE_BTOP"; + case REASON_PROC_STATE_FGS: + return "PROC_STATE_FGS"; + case REASON_PROC_STATE_BFGS: + return "PROC_STATE_BFGS"; + case REASON_UID_VISIBLE: + return "UID_VISIBLE"; + case REASON_SYSTEM_UID: + return "SYSTEM_UID"; + case REASON_ACTIVITY_STARTER: + return "ACTIVITY_STARTER"; + case REASON_START_ACTIVITY_FLAG: + return "START_ACTIVITY_FLAG"; + case REASON_FGS_BINDING: + return "FGS_BINDING"; + case REASON_DEVICE_OWNER: + return "DEVICE_OWNER"; + case REASON_PROFILE_OWNER: + return "PROFILE_OWNER"; + case REASON_COMPANION_DEVICE_MANAGER: + return "COMPANION_DEVICE_MANAGER"; + case REASON_BACKGROUND_ACTIVITY_PERMISSION: + return "BACKGROUND_ACTIVITY_PERMISSION"; + case REASON_BACKGROUND_FGS_PERMISSION: + return "BACKGROUND_FGS_PERMISSION"; + case REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION: + return "INSTR_BACKGROUND_ACTIVITY_PERMISSION"; + case REASON_INSTR_BACKGROUND_FGS_PERMISSION: + return "INSTR_BACKGROUND_FGS_PERMISSION"; + case REASON_SYSTEM_ALERT_WINDOW_PERMISSION: + return "SYSTEM_ALERT_WINDOW_PERMISSION"; + case REASON_DEVICE_DEMO_MODE: + return "DEVICE_DEMO_MODE"; + case REASON_EXEMPTED_PACKAGE: + return "EXEMPTED_PACKAGE"; + case REASON_ALLOWLISTED_PACKAGE: + return "ALLOWLISTED_PACKAGE"; + case REASON_APPOP: + return "APPOP"; + case REASON_GEOFENCING: + return "GEOFENCING"; + case REASON_PUSH_MESSAGING: + return "PUSH_MESSAGING"; + case REASON_ACTIVITY_RECOGNITION: + return "ACTIVITY_RECOGNITION"; + case REASON_BOOT_COMPLETED: + return "BOOT_COMPLETED"; + case REASON_PRE_BOOT_COMPLETED: + return "PRE_BOOT_COMPLETED"; + case REASON_LOCKED_BOOT_COMPLETED: + return "LOCKED_BOOT_COMPLETED"; + case REASON_SYSTEM_ALLOW_LISTED: + return "SYSTEM_ALLOW_LISTED"; + case REASON_ALARM_MANAGER_ALARM_CLOCK: + return "ALARM_MANAGER_ALARM_CLOCK"; + case REASON_ALARM_MANAGER_WHILE_IDLE: + return "ALARM_MANAGER_WHILE_IDLE"; + case REASON_SERVICE_LAUNCH: + return "SERVICE_LAUNCH"; + case REASON_KEY_CHAIN: + return "KEY_CHAIN"; + case REASON_PACKAGE_VERIFIER: + return "PACKAGE_VERIFIER"; + case REASON_SYNC_MANAGER: + return "SYNC_MANAGER"; + case REASON_DOMAIN_VERIFICATION_V1: + return "DOMAIN_VERIFICATION_V1"; + case REASON_DOMAIN_VERIFICATION_V2: + return "DOMAIN_VERIFICATION_V2"; + case REASON_VPN: + return "VPN"; + case REASON_NOTIFICATION_SERVICE: + return "NOTIFICATION_SERVICE"; + case REASON_PACKAGE_REPLACED: + return "PACKAGE_REPLACED"; + case REASON_LOCATION_PROVIDER: + return "LOCATION_PROVIDER"; + case REASON_MEDIA_BUTTON: + return "MEDIA_BUTTON"; + case REASON_EVENT_SMS: + return "EVENT_SMS"; + case REASON_EVENT_MMS: + return "EVENT_MMS"; + case REASON_SHELL: + return "SHELL"; + default: + return "(unknown:" + reasonCode + ")"; + } + } } diff --git a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java index e045b0fa3a6b..5e5717d11432 100644 --- a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java @@ -16,6 +16,8 @@ package com.android.server; +import android.annotation.Nullable; +import android.os.PowerWhitelistManager.ReasonCode; import android.os.PowerWhitelistManager.TempAllowListType; import com.android.server.deviceidle.IDeviceIdleConstraint; @@ -34,6 +36,10 @@ public interface DeviceIdleInternal { void addPowerSaveTempWhitelistApp(int callingUid, String packageName, long duration, int userId, boolean sync, String reason); + void addPowerSaveTempWhitelistApp(int callingUid, String packageName, + long duration, int userId, boolean sync, @ReasonCode int reasonCode, + @Nullable String reason); + /** * Called by ActivityManagerService to directly add UID to DeviceIdleController's temp * allowlist. @@ -41,11 +47,12 @@ public interface DeviceIdleInternal { * @param duration duration in milliseconds * @param type temp allowlist type defined at {@link TempAllowListType} * @param sync + * @param reasonCode one of {@link ReasonCode} * @param reason */ void addPowerSaveTempWhitelistAppDirect(int uid, long duration, - @TempAllowListType int type, boolean sync, - String reason); + @TempAllowListType int type, boolean sync, @ReasonCode int reasonCode, + @Nullable String reason); // duration in milliseconds long getNotificationAllowlistDuration(); diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index 8f7f705163ba..ac28e828eb2e 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -16,12 +16,17 @@ package com.android.server; +import static android.os.PowerWhitelistManager.REASON_SHELL; +import static android.os.PowerWhitelistManager.REASON_UNKNOWN; +import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED; +import static android.os.Process.INVALID_UID; + import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AlarmManager; -import android.app.BroadcastOptions; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -56,6 +61,7 @@ import android.os.Message; import android.os.PowerManager; import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; +import android.os.PowerWhitelistManager.ReasonCode; import android.os.PowerWhitelistManager.TempAllowListType; import android.os.Process; import android.os.RemoteException; @@ -1838,31 +1844,34 @@ public class DeviceIdleController extends SystemService } @Override - public long whitelistAppTemporarily(String packageName, int userId, String reason) - throws RemoteException { + public long whitelistAppTemporarily(String packageName, int userId, + @ReasonCode int reasonCode, @Nullable String reason) throws RemoteException { // At least 10 seconds. long durationMs = Math.max(10_000L, mConstants.MAX_TEMP_APP_ALLOWLIST_DURATION_MS / 2); - addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reason); + addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reasonCode, + reason); return durationMs; } @Override - public void addPowerSaveTempWhitelistApp(String packageName, long duration, - int userId, String reason) throws RemoteException { - addPowerSaveTempAllowlistAppChecked(packageName, duration, userId, reason); + public void addPowerSaveTempWhitelistApp(String packageName, long duration, int userId, + @ReasonCode int reasonCode, @Nullable String reason) throws RemoteException { + addPowerSaveTempAllowlistAppChecked(packageName, duration, userId, reasonCode, reason); } - @Override public long addPowerSaveTempWhitelistAppForMms(String packageName, - int userId, String reason) throws RemoteException { + @Override public long addPowerSaveTempWhitelistAppForMms(String packageName, int userId, + @ReasonCode int reasonCode, @Nullable String reason) throws RemoteException { long durationMs = mConstants.MMS_TEMP_APP_ALLOWLIST_DURATION_MS; - addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reason); + addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reasonCode, + reason); return durationMs; } - @Override public long addPowerSaveTempWhitelistAppForSms(String packageName, - int userId, String reason) throws RemoteException { + @Override public long addPowerSaveTempWhitelistAppForSms(String packageName, int userId, + @ReasonCode int reasonCode, @Nullable String reason) throws RemoteException { long durationMs = mConstants.SMS_TEMP_APP_ALLOWLIST_DURATION_MS; - addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reason); + addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reasonCode, + reason); return durationMs; } @@ -1934,18 +1943,29 @@ public class DeviceIdleController extends SystemService } // duration in milliseconds + @Deprecated @Override public void addPowerSaveTempWhitelistApp(int callingUid, String packageName, - long duration, int userId, boolean sync, String reason) { + long duration, int userId, boolean sync, @Nullable String reason) { addPowerSaveTempAllowlistAppInternal(callingUid, packageName, duration, - userId, sync, reason); + userId, sync, REASON_UNKNOWN, reason); + } + + @Override + public void addPowerSaveTempWhitelistApp(int callingUid, String packageName, + long duration, int userId, boolean sync, @ReasonCode int reasonCode, + @Nullable String reason) { + addPowerSaveTempAllowlistAppInternal(callingUid, packageName, duration, + userId, sync, reasonCode, reason); } // duration in milliseconds @Override public void addPowerSaveTempWhitelistAppDirect(int uid, long duration, - @TempAllowListType int type, boolean sync, String reason) { - addPowerSaveTempWhitelistAppDirectInternal(0, uid, duration, type, sync, reason); + @TempAllowListType int type, boolean sync, @ReasonCode int reasonCode, + @Nullable String reason) { + addPowerSaveTempWhitelistAppDirectInternal(0, uid, duration, type, sync, + reasonCode, reason); } // duration in milliseconds @@ -2293,7 +2313,7 @@ public class DeviceIdleController extends SystemService filter.addAction(Intent.ACTION_SCREEN_ON); getContext().registerReceiver(mInteractivityReceiver, filter); - mLocalActivityManager.setDeviceIdleWhitelist( + mLocalActivityManager.setDeviceIdleAllowlist( mPowerSaveWhitelistAllAppIdArray, mPowerSaveWhitelistExceptIdleAppIdArray); mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray); @@ -2671,7 +2691,8 @@ public class DeviceIdleController extends SystemService } void addPowerSaveTempAllowlistAppChecked(String packageName, long duration, - int userId, String reason) throws RemoteException { + int userId, @ReasonCode int reasonCode, @Nullable String reason) + throws RemoteException { getContext().enforceCallingPermission( Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, "No permission to change device idle whitelist"); @@ -2686,7 +2707,7 @@ public class DeviceIdleController extends SystemService final long token = Binder.clearCallingIdentity(); try { addPowerSaveTempAllowlistAppInternal(callingUid, - packageName, duration, userId, true, reason); + packageName, duration, userId, true, reasonCode, reason); } finally { Binder.restoreCallingIdentity(token); } @@ -2718,12 +2739,12 @@ public class DeviceIdleController extends SystemService * app an exemption to access network and acquire wakelocks. */ void addPowerSaveTempAllowlistAppInternal(int callingUid, String packageName, - long duration, int userId, boolean sync, String reason) { + long duration, int userId, boolean sync, @ReasonCode int reasonCode, + @Nullable String reason) { try { int uid = getContext().getPackageManager().getPackageUidAsUser(packageName, userId); addPowerSaveTempWhitelistAppDirectInternal(callingUid, uid, duration, - BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED, sync, - reason); + TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, sync, reasonCode, reason); } catch (NameNotFoundException e) { } } @@ -2733,7 +2754,8 @@ public class DeviceIdleController extends SystemService * app an exemption to access network and acquire wakelocks. */ void addPowerSaveTempWhitelistAppDirectInternal(int callingUid, int uid, - long duration, @TempAllowListType int type, boolean sync, String reason) { + long duration, @TempAllowListType int type, boolean sync, @ReasonCode int reasonCode, + @Nullable String reason) { final long timeNow = SystemClock.elapsedRealtime(); boolean informWhitelistChanged = false; int appId = UserHandle.getAppId(uid); @@ -2765,7 +2787,8 @@ public class DeviceIdleController extends SystemService } catch (RemoteException e) { } postTempActiveTimeoutMessage(uid, duration); - updateTempWhitelistAppIdsLocked(uid, true, duration, type); + updateTempWhitelistAppIdsLocked(uid, true, duration, type, reasonCode, + reason, callingUid); if (sync) { informWhitelistChanged = true; } else { @@ -2844,12 +2867,13 @@ public class DeviceIdleController extends SystemService } @GuardedBy("this") - private void onAppRemovedFromTempWhitelistLocked(int uid, String reason) { + private void onAppRemovedFromTempWhitelistLocked(int uid, @Nullable String reason) { if (DEBUG) { Slog.d(TAG, "Removing uid " + uid + " from temp whitelist"); } final int appId = UserHandle.getAppId(uid); - updateTempWhitelistAppIdsLocked(uid, false, 0, 0); + updateTempWhitelistAppIdsLocked(uid, false, 0, 0, REASON_UNKNOWN, + reason, INVALID_UID); mHandler.obtainMessage(MSG_REPORT_TEMP_APP_WHITELIST_CHANGED_TO_NPMS, appId, 0) .sendToTarget(); reportTempWhitelistChangedLocked(uid, false); @@ -3860,7 +3884,7 @@ public class DeviceIdleController extends SystemService mPowerSaveWhitelistUserAppIdArray = buildAppIdArray(null, mPowerSaveWhitelistUserApps, mPowerSaveWhitelistUserAppIds); if (mLocalActivityManager != null) { - mLocalActivityManager.setDeviceIdleWhitelist( + mLocalActivityManager.setDeviceIdleAllowlist( mPowerSaveWhitelistAllAppIdArray, mPowerSaveWhitelistExceptIdleAppIdArray); } if (mLocalPowerManager != null) { @@ -3880,9 +3904,14 @@ public class DeviceIdleController extends SystemService * @param durationMs duration in milliseconds to add to temp allowlist, only valid when * param adding is true. * @param type temp allowlist type defined at {@link TempAllowListType} + * @prama reasonCode one of {@Link ReasonCode} + * @param reason A human-readable reason for logging purposes. + * @param callingUid the callingUid that setup this temp-allowlist, only valid when param adding + * is true. */ private void updateTempWhitelistAppIdsLocked(int uid, boolean adding, long durationMs, - @TempAllowListType int type) { + @TempAllowListType int type, @ReasonCode int reasonCode, @Nullable String reason, + int callingUid) { final int size = mTempWhitelistAppIdEndTimes.size(); if (mTempWhitelistAppIdArray.length != size) { mTempWhitelistAppIdArray = new int[size]; @@ -3895,8 +3924,8 @@ public class DeviceIdleController extends SystemService Slog.d(TAG, "Setting activity manager temp whitelist to " + Arrays.toString(mTempWhitelistAppIdArray)); } - mLocalActivityManager.updateDeviceIdleTempWhitelist(mTempWhitelistAppIdArray, uid, - adding, durationMs, type); + mLocalActivityManager.updateDeviceIdleTempAllowlist(mTempWhitelistAppIdArray, uid, + adding, durationMs, type, reasonCode, reason, callingUid); } if (mLocalPowerManager != null) { if (DEBUG) { @@ -4428,7 +4457,8 @@ public class DeviceIdleController extends SystemService if (removePkg) { removePowerSaveTempAllowlistAppChecked(arg, shell.userId); } else { - addPowerSaveTempAllowlistAppChecked(arg, duration, shell.userId, "shell"); + addPowerSaveTempAllowlistAppChecked(arg, duration, shell.userId, + REASON_SHELL, "shell"); } } catch (Exception e) { pw.println("Failed: " + e); 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 ac6eb3229a25..040a1164fc73 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -151,6 +151,8 @@ public class JobSchedulerService extends com.android.server.SystemService private static final boolean ENFORCE_MAX_JOBS = true; /** 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 = 20; @VisibleForTesting public static Clock sSystemClock = Clock.systemUTC(); @@ -297,6 +299,10 @@ public class JobSchedulerService extends com.android.server.SystemService */ boolean mReportedActive; + private int mLastCompletedJobIndex = 0; + private final JobStatus[] mLastCompletedJobs = new JobStatus[NUM_COMPLETED_JOB_HISTORY]; + private final long[] mLastCompletedJobTimeElapsed = new long[NUM_COMPLETED_JOB_HISTORY]; + /** * A mapping of which uids are currently in the foreground to their effective priority. */ @@ -881,8 +887,10 @@ public class JobSchedulerService extends com.android.server.SystemService } @Override - public void onUserStarting(@NonNull TargetUser user) { + public void onUserUnlocked(@NonNull TargetUser user) { synchronized (mLock) { + // Note that the user has started after its unlocked instead of when the user + // actually starts because the storage won't be decrypted until unlock. mStartedUsers = ArrayUtils.appendInt(mStartedUsers, user.getUserIdentifier()); } // Let's kick any outstanding jobs for this user. @@ -890,12 +898,6 @@ public class JobSchedulerService extends com.android.server.SystemService } @Override - public void onUserUnlocking(@NonNull TargetUser user) { - // Let's kick any outstanding jobs for this user. - mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); - } - - @Override public void onUserStopping(@NonNull TargetUser user) { synchronized (mLock) { mStartedUsers = ArrayUtils.removeInt(mStartedUsers, user.getUserIdentifier()); @@ -1752,6 +1754,10 @@ public class JobSchedulerService extends com.android.server.SystemService Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule); } + mLastCompletedJobs[mLastCompletedJobIndex] = jobStatus; + mLastCompletedJobTimeElapsed[mLastCompletedJobIndex] = sElapsedRealtimeClock.millis(); + mLastCompletedJobIndex = (mLastCompletedJobIndex + 1) % NUM_COMPLETED_JOB_HISTORY; + // Intentionally not checking expedited job quota here. An app can't find out if it's run // out of quota when it asks JS to reschedule an expedited job. Instead, the rescheduled // EJ will just be demoted to a regular job if the app has no EJ quota left. @@ -3298,6 +3304,37 @@ public class JobSchedulerService extends com.android.server.SystemService } } pw.decreaseIndent(); + + pw.println(); + boolean recentPrinted = false; + pw.println("Recently completed jobs:"); + pw.increaseIndent(); + for (int r = 1; r <= NUM_COMPLETED_JOB_HISTORY; ++r) { + // Print most recent first + final int idx = (mLastCompletedJobIndex + NUM_COMPLETED_JOB_HISTORY - r) + % NUM_COMPLETED_JOB_HISTORY; + final JobStatus job = mLastCompletedJobs[idx]; + if (job != null) { + if (!predicate.test(job)) { + continue; + } + recentPrinted = true; + TimeUtils.formatDuration(mLastCompletedJobTimeElapsed[idx], nowElapsed, pw); + pw.println(); + // Double indent for readability + pw.increaseIndent(); + pw.increaseIndent(); + job.dump(pw, true, nowElapsed); + pw.decreaseIndent(); + pw.decreaseIndent(); + } + } + if (!recentPrinted) { + pw.println("None"); + } + pw.decreaseIndent(); + pw.println(); + if (filterUid == -1) { pw.println(); pw.print("mReadyToRock="); pw.println(mReadyToRock); 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/api/Android.bp b/api/Android.bp index 15c1dfcfe153..1fdf1771bb13 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -40,6 +40,7 @@ genrule { ":art.module.public.api{.public.api.txt}", ":conscrypt.module.public.api{.public.api.txt}", ":framework-appsearch{.public.api.txt}", + ":framework-connectivity{.public.api.txt}", ":framework-graphics{.public.api.txt}", ":framework-media{.public.api.txt}", ":framework-mediaprovider{.public.api.txt}", @@ -95,6 +96,7 @@ genrule { ":art.module.public.api{.public.stubs.source}", ":conscrypt.module.public.api{.public.stubs.source}", ":framework-appsearch{.public.stubs.source}", + ":framework-connectivity{.public.stubs.source}", ":framework-graphics{.public.stubs.source}", ":framework-media{.public.stubs.source}", ":framework-mediaprovider{.public.stubs.source}", @@ -120,6 +122,7 @@ genrule { ":art.module.public.api{.public.removed-api.txt}", ":conscrypt.module.public.api{.public.removed-api.txt}", ":framework-appsearch{.public.removed-api.txt}", + ":framework-connectivity{.public.removed-api.txt}", ":framework-graphics{.public.removed-api.txt}", ":framework-media{.public.removed-api.txt}", ":framework-mediaprovider{.public.removed-api.txt}", @@ -155,6 +158,7 @@ genrule { srcs: [ ":android.net.ipsec.ike{.system.api.txt}", ":framework-appsearch{.system.api.txt}", + ":framework-connectivity{.system.api.txt}", ":framework-graphics{.system.api.txt}", ":framework-media{.system.api.txt}", ":framework-mediaprovider{.system.api.txt}", @@ -208,6 +212,7 @@ genrule { srcs: [ ":android.net.ipsec.ike{.system.removed-api.txt}", ":framework-appsearch{.system.removed-api.txt}", + ":framework-connectivity{.system.removed-api.txt}", ":framework-graphics{.system.removed-api.txt}", ":framework-media{.system.removed-api.txt}", ":framework-mediaprovider{.system.removed-api.txt}", @@ -243,6 +248,7 @@ genrule { srcs: [ ":android.net.ipsec.ike{.module-lib.api.txt}", ":framework-appsearch{.module-lib.api.txt}", + ":framework-connectivity{.module-lib.api.txt}", ":framework-graphics{.module-lib.api.txt}", ":framework-media{.module-lib.api.txt}", ":framework-mediaprovider{.module-lib.api.txt}", @@ -298,6 +304,7 @@ genrule { srcs: [ ":android.net.ipsec.ike{.module-lib.removed-api.txt}", ":framework-appsearch{.module-lib.removed-api.txt}", + ":framework-connectivity{.module-lib.removed-api.txt}", ":framework-graphics{.module-lib.removed-api.txt}", ":framework-media{.module-lib.removed-api.txt}", ":framework-mediaprovider{.module-lib.removed-api.txt}", @@ -340,3 +347,49 @@ genrule { out: ["combined-removed-dex.txt"], cmd: "$(location gen_combined_removed_dex.sh) $(location metalava) $(genDir) $(in) > $(out)", } + +genrule { + name: "services-system-server-current.txt", + srcs: [ + ":service-permission{.system-server.api.txt}", + ":non-updatable-system-server-current.txt", + ], + out: ["system-server-current.txt"], + tools: ["metalava"], + cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)", + dists: [ + { + targets: ["droidcore"], + dir: "api", + dest: "system-server-current.txt", + }, + { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/system-server/api", + dest: "merge-android.txt", + }, + ], +} + +genrule { + name: "services-system-server-removed.txt", + srcs: [ + ":service-permission{.system-server.removed-api.txt}", + ":non-updatable-system-server-removed.txt", + ], + out: ["system-server-removed.txt"], + tools: ["metalava"], + cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)", + dists: [ + { + targets: ["droidcore"], + dir: "api", + dest: "system-server-removed.txt", + }, + { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/system-server/api", + dest: "merge-removed.txt", + }, + ], +} 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/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp index 422c2be44251..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,10 +114,18 @@ void DeviceCallback::onDeviceGetReport(uint32_t requestId, uint8_t reportId) { checkAndClearException(env, "onDeviceGetReport"); } -void DeviceCallback::onDeviceOutput(uint8_t eventId, uint8_t rType, +void DeviceCallback::onDeviceSetReport(uint8_t rType, const std::vector<uint8_t>& data) { JNIEnv* env = getJNIEnv(); - env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOutput, eventId, rType, + 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()); checkAndClearException(env, "onDeviceOutput"); } @@ -262,7 +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->onDeviceOutput(UHID_SET_REPORT, set_report.rtype, data); + mDeviceCallback->onDeviceSetReport(set_report.rtype, data); break; } case UHID_OUTPUT: { @@ -271,7 +280,7 @@ int Device::handleEvents(int events) { if (DEBUG_OUTPUT) { ALOGD("UHID_OUTPUT rtype=%" PRIu8 " data=%s", output.rtype, toString(data).c_str()); } - mDeviceCallback->onDeviceOutput(UHID_OUTPUT, output.rtype, data); + mDeviceCallback->onDeviceOutput(output.rtype, data); break; } default: { @@ -366,8 +375,10 @@ 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", "(BB[B)V"); + env->GetMethodID(clazz, "onDeviceOutput", "(B[B)V"); uhid::gDeviceCallbackClassInfo.onDeviceError = env->GetMethodID(clazz, "onDeviceError", "()V"); if (uhid::gDeviceCallbackClassInfo.onDeviceOpen == NULL || diff --git a/cmds/hid/jni/com_android_commands_hid_Device.h b/cmds/hid/jni/com_android_commands_hid_Device.h index bb73132cc20d..d10a9aa3680c 100644 --- a/cmds/hid/jni/com_android_commands_hid_Device.h +++ b/cmds/hid/jni/com_android_commands_hid_Device.h @@ -31,7 +31,8 @@ public: void onDeviceOpen(); void onDeviceGetReport(uint32_t requestId, uint8_t reportId); - void onDeviceOutput(uint8_t eventId, uint8_t rType, const std::vector<uint8_t>& data); + void onDeviceSetReport(uint8_t rType, const std::vector<uint8_t>& data); + void onDeviceOutput(uint8_t rType, const std::vector<uint8_t>& data); void onDeviceError(); private: diff --git a/cmds/hid/src/com/android/commands/hid/Device.java b/cmds/hid/src/com/android/commands/hid/Device.java index 37d0b1c6bfa1..95b1e9a7b57f 100644 --- a/cmds/hid/src/com/android/commands/hid/Device.java +++ b/cmds/hid/src/com/android/commands/hid/Device.java @@ -199,8 +199,8 @@ public class Device { mHandler.sendMessageAtTime(msg, mTimeToSend); } - // native callback - public void onDeviceOutput(byte eventId, 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", eventId); @@ -221,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/core/api/current.txt b/core/api/current.txt index 160ad7a4d555..5f6c9b684409 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 @@ -6269,14 +6270,14 @@ package android.app { method public static android.app.PendingIntent getActivities(android.content.Context, int, @NonNull android.content.Intent[], int, @Nullable android.os.Bundle); method public static android.app.PendingIntent getActivity(android.content.Context, int, android.content.Intent, int); method public static android.app.PendingIntent getActivity(android.content.Context, int, @NonNull android.content.Intent, int, @Nullable android.os.Bundle); - method public static android.app.PendingIntent getBroadcast(android.content.Context, int, android.content.Intent, int); - method @Nullable public String getCreatorPackage(); + method public static android.app.PendingIntent getBroadcast(android.content.Context, int, @NonNull android.content.Intent, int); + method @NonNull public String getCreatorPackage(); method public int getCreatorUid(); - method @Nullable public android.os.UserHandle getCreatorUserHandle(); + method @NonNull public android.os.UserHandle getCreatorUserHandle(); method public static android.app.PendingIntent getForegroundService(android.content.Context, int, @NonNull android.content.Intent, int); - method public android.content.IntentSender getIntentSender(); + method @NonNull public android.content.IntentSender getIntentSender(); method public static android.app.PendingIntent getService(android.content.Context, int, @NonNull android.content.Intent, int); - method @Deprecated public String getTargetPackage(); + method @Deprecated @NonNull public String getTargetPackage(); method public boolean isActivity(); method public boolean isBroadcast(); method public boolean isForegroundService(); @@ -9740,7 +9741,7 @@ package android.companion { public interface DeviceFilter<D extends android.os.Parcelable> extends android.os.Parcelable { } - public class DeviceNotAssociatedException extends java.lang.Exception { + public class DeviceNotAssociatedException extends java.lang.RuntimeException { } public final class WifiDeviceFilter implements android.companion.DeviceFilter<android.net.wifi.ScanResult> { @@ -10388,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[]); @@ -18895,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(); @@ -19660,6 +19662,7 @@ package android.location { method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void addProximityAlert(double, double, float, long, @NonNull android.app.PendingIntent); method public void addTestProvider(@NonNull String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int); method public void addTestProvider(@NonNull String, @NonNull android.location.provider.ProviderProperties); + method public void addTestProvider(@NonNull String, @NonNull android.location.provider.ProviderProperties, @NonNull java.util.Set<java.lang.String>); method @Deprecated public void clearTestProviderEnabled(@NonNull String); method @Deprecated public void clearTestProviderLocation(@NonNull String); method @Deprecated public void clearTestProviderStatus(@NonNull String); @@ -22148,6 +22151,14 @@ package android.media { field public static final String KEY_TILE_HEIGHT = "tile-height"; field public static final String KEY_TILE_WIDTH = "tile-width"; field public static final String KEY_TRACK_ID = "track-id"; + field public static final String KEY_VIDEO_QP_B_MAX = "video-qp-b-max"; + field public static final String KEY_VIDEO_QP_B_MIN = "video-qp-b-min"; + field public static final String KEY_VIDEO_QP_I_MAX = "video-qp-i-max"; + field public static final String KEY_VIDEO_QP_I_MIN = "video-qp-i-min"; + field public static final String KEY_VIDEO_QP_MAX = "video-qp-max"; + field public static final String KEY_VIDEO_QP_MIN = "video-qp-min"; + field public static final String KEY_VIDEO_QP_P_MAX = "video-qp-p-max"; + field public static final String KEY_VIDEO_QP_P_MIN = "video-qp-p-min"; field public static final String KEY_WIDTH = "width"; field public static final String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm"; field public static final String MIMETYPE_AUDIO_AC3 = "audio/ac3"; @@ -25816,161 +25827,6 @@ package android.mtp { package android.net { - public class CaptivePortal implements android.os.Parcelable { - method public int describeContents(); - method public void ignoreNetwork(); - method public void reportCaptivePortalDismissed(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortal> CREATOR; - } - - public class ConnectivityDiagnosticsManager { - method public void registerConnectivityDiagnosticsCallback(@NonNull android.net.NetworkRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback); - method public void unregisterConnectivityDiagnosticsCallback(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback); - } - - public abstract static class ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback { - ctor public ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback(); - method public void onConnectivityReportAvailable(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityReport); - method public void onDataStallSuspected(@NonNull android.net.ConnectivityDiagnosticsManager.DataStallReport); - method public void onNetworkConnectivityReported(@NonNull android.net.Network, boolean); - } - - public static final class ConnectivityDiagnosticsManager.ConnectivityReport implements android.os.Parcelable { - ctor public ConnectivityDiagnosticsManager.ConnectivityReport(@NonNull android.net.Network, long, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle); - method public int describeContents(); - method @NonNull public android.os.PersistableBundle getAdditionalInfo(); - method @NonNull public android.net.LinkProperties getLinkProperties(); - method @NonNull public android.net.Network getNetwork(); - method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities(); - method public long getReportTimestamp(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.ConnectivityReport> CREATOR; - field public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = "networkProbesAttempted"; - field public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK = "networkProbesSucceeded"; - field public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult"; - field public static final int NETWORK_PROBE_DNS = 4; // 0x4 - field public static final int NETWORK_PROBE_FALLBACK = 32; // 0x20 - field public static final int NETWORK_PROBE_HTTP = 8; // 0x8 - field public static final int NETWORK_PROBE_HTTPS = 16; // 0x10 - field public static final int NETWORK_PROBE_PRIVATE_DNS = 64; // 0x40 - field public static final int NETWORK_VALIDATION_RESULT_INVALID = 0; // 0x0 - field public static final int NETWORK_VALIDATION_RESULT_PARTIALLY_VALID = 2; // 0x2 - field public static final int NETWORK_VALIDATION_RESULT_SKIPPED = 3; // 0x3 - field public static final int NETWORK_VALIDATION_RESULT_VALID = 1; // 0x1 - } - - public static final class ConnectivityDiagnosticsManager.DataStallReport implements android.os.Parcelable { - ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle); - method public int describeContents(); - method public int getDetectionMethod(); - method @NonNull public android.net.LinkProperties getLinkProperties(); - method @NonNull public android.net.Network getNetwork(); - method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities(); - method public long getReportTimestamp(); - method @NonNull public android.os.PersistableBundle getStallDetails(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.DataStallReport> CREATOR; - field public static final int DETECTION_METHOD_DNS_EVENTS = 1; // 0x1 - field public static final int DETECTION_METHOD_TCP_METRICS = 2; // 0x2 - field public static final String KEY_DNS_CONSECUTIVE_TIMEOUTS = "dnsConsecutiveTimeouts"; - field public static final String KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS = "tcpMetricsCollectionPeriodMillis"; - field public static final String KEY_TCP_PACKET_FAIL_RATE = "tcpPacketFailRate"; - } - - public class ConnectivityManager { - method public void addDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener); - method public boolean bindProcessToNetwork(@Nullable android.net.Network); - method @NonNull public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull android.net.IpSecManager.UdpEncapsulationSocket, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback); - method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network getActiveNetwork(); - method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getActiveNetworkInfo(); - method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo[] getAllNetworkInfo(); - method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network[] getAllNetworks(); - method @Deprecated public boolean getBackgroundDataSetting(); - method @Nullable public android.net.Network getBoundNetworkForProcess(); - method public int getConnectionOwnerUid(int, @NonNull java.net.InetSocketAddress, @NonNull java.net.InetSocketAddress); - method @Nullable public android.net.ProxyInfo getDefaultProxy(); - method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.LinkProperties getLinkProperties(@Nullable android.net.Network); - method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public int getMultipathPreference(@Nullable android.net.Network); - method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkCapabilities getNetworkCapabilities(@Nullable android.net.Network); - method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getNetworkInfo(int); - method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getNetworkInfo(@Nullable android.net.Network); - method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public int getNetworkPreference(); - method @Nullable public byte[] getNetworkWatchlistConfigHash(); - method @Deprecated @Nullable public static android.net.Network getProcessDefaultNetwork(); - method public int getRestrictBackgroundStatus(); - method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public boolean isActiveNetworkMetered(); - method public boolean isDefaultNetworkActive(); - method @Deprecated public static boolean isNetworkTypeValid(int); - method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback); - method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); - method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback); - method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); - method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.app.PendingIntent); - method public void releaseNetworkRequest(@NonNull android.app.PendingIntent); - method public void removeDefaultNetworkActiveListener(@NonNull android.net.ConnectivityManager.OnNetworkActiveListener); - method @Deprecated public void reportBadNetwork(@Nullable android.net.Network); - method public void reportNetworkConnectivity(@Nullable android.net.Network, boolean); - method public boolean requestBandwidthUpdate(@NonNull android.net.Network); - method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback); - method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); - method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, int); - method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler, int); - method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.app.PendingIntent); - method @Deprecated public void setNetworkPreference(int); - method @Deprecated public static boolean setProcessDefaultNetwork(@Nullable android.net.Network); - method public void unregisterNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback); - method public void unregisterNetworkCallback(@NonNull android.app.PendingIntent); - field @Deprecated public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED"; - field public static final String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL"; - field public static final String ACTION_RESTRICT_BACKGROUND_CHANGED = "android.net.conn.RESTRICT_BACKGROUND_CHANGED"; - field @Deprecated public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; - field @Deprecated public static final int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1 - field public static final String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL"; - field public static final String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL"; - field @Deprecated public static final String EXTRA_EXTRA_INFO = "extraInfo"; - field @Deprecated public static final String EXTRA_IS_FAILOVER = "isFailover"; - field public static final String EXTRA_NETWORK = "android.net.extra.NETWORK"; - field @Deprecated public static final String EXTRA_NETWORK_INFO = "networkInfo"; - field public static final String EXTRA_NETWORK_REQUEST = "android.net.extra.NETWORK_REQUEST"; - field @Deprecated public static final String EXTRA_NETWORK_TYPE = "networkType"; - field public static final String EXTRA_NO_CONNECTIVITY = "noConnectivity"; - field @Deprecated public static final String EXTRA_OTHER_NETWORK_INFO = "otherNetwork"; - field public static final String EXTRA_REASON = "reason"; - field public static final int MULTIPATH_PREFERENCE_HANDOVER = 1; // 0x1 - field public static final int MULTIPATH_PREFERENCE_PERFORMANCE = 4; // 0x4 - field public static final int MULTIPATH_PREFERENCE_RELIABILITY = 2; // 0x2 - field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1 - field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3 - field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2 - field @Deprecated public static final int TYPE_BLUETOOTH = 7; // 0x7 - field @Deprecated public static final int TYPE_DUMMY = 8; // 0x8 - field @Deprecated public static final int TYPE_ETHERNET = 9; // 0x9 - field @Deprecated public static final int TYPE_MOBILE = 0; // 0x0 - field @Deprecated public static final int TYPE_MOBILE_DUN = 4; // 0x4 - field @Deprecated public static final int TYPE_MOBILE_HIPRI = 5; // 0x5 - field @Deprecated public static final int TYPE_MOBILE_MMS = 2; // 0x2 - field @Deprecated public static final int TYPE_MOBILE_SUPL = 3; // 0x3 - field @Deprecated public static final int TYPE_VPN = 17; // 0x11 - field @Deprecated public static final int TYPE_WIFI = 1; // 0x1 - field @Deprecated public static final int TYPE_WIMAX = 6; // 0x6 - } - - public static class ConnectivityManager.NetworkCallback { - ctor public ConnectivityManager.NetworkCallback(); - method public void onAvailable(@NonNull android.net.Network); - method public void onBlockedStatusChanged(@NonNull android.net.Network, boolean); - method public void onCapabilitiesChanged(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities); - method public void onLinkPropertiesChanged(@NonNull android.net.Network, @NonNull android.net.LinkProperties); - method public void onLosing(@NonNull android.net.Network, int); - method public void onLost(@NonNull android.net.Network); - method public void onUnavailable(); - } - - public static interface ConnectivityManager.OnNetworkActiveListener { - method public void onNetworkActive(); - } - public class Credentials { ctor public Credentials(int, int, int); method public int getGid(); @@ -25978,46 +25834,6 @@ package android.net { method public int getUid(); } - public class DhcpInfo implements android.os.Parcelable { - ctor public DhcpInfo(); - method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.DhcpInfo> CREATOR; - field public int dns1; - field public int dns2; - field public int gateway; - field public int ipAddress; - field public int leaseDuration; - field public int netmask; - field public int serverAddress; - } - - public final class DnsResolver { - method @NonNull public static android.net.DnsResolver getInstance(); - method public void query(@Nullable android.net.Network, @NonNull String, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super java.util.List<java.net.InetAddress>>); - method public void query(@Nullable android.net.Network, @NonNull String, int, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super java.util.List<java.net.InetAddress>>); - method public void rawQuery(@Nullable android.net.Network, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super byte[]>); - method public void rawQuery(@Nullable android.net.Network, @NonNull String, int, int, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super byte[]>); - field public static final int CLASS_IN = 1; // 0x1 - field public static final int ERROR_PARSE = 0; // 0x0 - field public static final int ERROR_SYSTEM = 1; // 0x1 - field public static final int FLAG_EMPTY = 0; // 0x0 - field public static final int FLAG_NO_CACHE_LOOKUP = 4; // 0x4 - field public static final int FLAG_NO_CACHE_STORE = 2; // 0x2 - field public static final int FLAG_NO_RETRY = 1; // 0x1 - field public static final int TYPE_A = 1; // 0x1 - field public static final int TYPE_AAAA = 28; // 0x1c - } - - public static interface DnsResolver.Callback<T> { - method public void onAnswer(@NonNull T, int); - method public void onError(@NonNull android.net.DnsResolver.DnsException); - } - - public static class DnsResolver.DnsException extends java.lang.Exception { - field public final int code; - } - public final class Ikev2VpnProfile extends android.net.PlatformVpnProfile { method @NonNull public java.util.List<java.lang.String> getAllowedAlgorithms(); method public int getMaxMtu(); @@ -26047,21 +25863,6 @@ package android.net { method @NonNull public android.net.Ikev2VpnProfile.Builder setProxy(@Nullable android.net.ProxyInfo); } - public class InetAddresses { - method public static boolean isNumericAddress(@NonNull String); - method @NonNull public static java.net.InetAddress parseNumericAddress(@NonNull String); - } - - public final class IpPrefix implements android.os.Parcelable { - method public boolean contains(@NonNull java.net.InetAddress); - method public int describeContents(); - method @NonNull public java.net.InetAddress getAddress(); - method @IntRange(from=0, to=128) public int getPrefixLength(); - method @NonNull public byte[] getRawAddress(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.IpPrefix> CREATOR; - } - public final class IpSecAlgorithm implements android.os.Parcelable { ctor public IpSecAlgorithm(@NonNull String, @NonNull byte[]); ctor public IpSecAlgorithm(@NonNull String, @NonNull byte[], int); @@ -26130,45 +25931,6 @@ package android.net { method @NonNull public android.net.IpSecTransform.Builder setIpv4Encapsulation(@NonNull android.net.IpSecManager.UdpEncapsulationSocket, int); } - public class LinkAddress implements android.os.Parcelable { - method public int describeContents(); - method public java.net.InetAddress getAddress(); - method public int getFlags(); - method @IntRange(from=0, to=128) public int getPrefixLength(); - method public int getScope(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.LinkAddress> CREATOR; - } - - public final class LinkProperties implements android.os.Parcelable { - ctor public LinkProperties(); - method public boolean addRoute(@NonNull android.net.RouteInfo); - method public void clear(); - method public int describeContents(); - method @Nullable public java.net.Inet4Address getDhcpServerAddress(); - method @NonNull public java.util.List<java.net.InetAddress> getDnsServers(); - method @Nullable public String getDomains(); - method @Nullable public android.net.ProxyInfo getHttpProxy(); - method @Nullable public String getInterfaceName(); - method @NonNull public java.util.List<android.net.LinkAddress> getLinkAddresses(); - method public int getMtu(); - method @Nullable public android.net.IpPrefix getNat64Prefix(); - method @Nullable public String getPrivateDnsServerName(); - method @NonNull public java.util.List<android.net.RouteInfo> getRoutes(); - method public boolean isPrivateDnsActive(); - method public boolean isWakeOnLanSupported(); - method public void setDhcpServerAddress(@Nullable java.net.Inet4Address); - method public void setDnsServers(@NonNull java.util.Collection<java.net.InetAddress>); - method public void setDomains(@Nullable String); - method public void setHttpProxy(@Nullable android.net.ProxyInfo); - method public void setInterfaceName(@Nullable String); - method public void setLinkAddresses(@NonNull java.util.Collection<android.net.LinkAddress>); - method public void setMtu(int); - method public void setNat64Prefix(@Nullable android.net.IpPrefix); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.LinkProperties> CREATOR; - } - public class LocalServerSocket implements java.io.Closeable { ctor public LocalServerSocket(String) throws java.io.IOException; ctor public LocalServerSocket(java.io.FileDescriptor) throws java.io.IOException; @@ -26224,24 +25986,6 @@ package android.net { enum_constant public static final android.net.LocalSocketAddress.Namespace RESERVED; } - public final class MacAddress implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public static android.net.MacAddress fromBytes(@NonNull byte[]); - method @NonNull public static android.net.MacAddress fromString(@NonNull String); - method public int getAddressType(); - method @Nullable public java.net.Inet6Address getLinkLocalIpv6FromEui48Mac(); - method public boolean isLocallyAssigned(); - method public boolean matches(@NonNull android.net.MacAddress, @NonNull android.net.MacAddress); - method @NonNull public byte[] toByteArray(); - method @NonNull public String toOuiString(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.net.MacAddress BROADCAST_ADDRESS; - field @NonNull public static final android.os.Parcelable.Creator<android.net.MacAddress> CREATOR; - field public static final int TYPE_BROADCAST = 3; // 0x3 - field public static final int TYPE_MULTICAST = 2; // 0x2 - field public static final int TYPE_UNICAST = 1; // 0x1 - } - public class MailTo { method public String getBody(); method public String getCc(); @@ -26253,139 +25997,6 @@ package android.net { field public static final String MAILTO_SCHEME = "mailto:"; } - public class Network implements android.os.Parcelable { - method public void bindSocket(java.net.DatagramSocket) throws java.io.IOException; - method public void bindSocket(java.net.Socket) throws java.io.IOException; - method public void bindSocket(java.io.FileDescriptor) throws java.io.IOException; - method public int describeContents(); - method public static android.net.Network fromNetworkHandle(long); - method public java.net.InetAddress[] getAllByName(String) throws java.net.UnknownHostException; - method public java.net.InetAddress getByName(String) throws java.net.UnknownHostException; - method public long getNetworkHandle(); - method public javax.net.SocketFactory getSocketFactory(); - method public java.net.URLConnection openConnection(java.net.URL) throws java.io.IOException; - method public java.net.URLConnection openConnection(java.net.URL, java.net.Proxy) throws java.io.IOException; - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.Network> CREATOR; - } - - public final class NetworkCapabilities implements android.os.Parcelable { - ctor public NetworkCapabilities(); - ctor public NetworkCapabilities(android.net.NetworkCapabilities); - method public int describeContents(); - method public int getLinkDownstreamBandwidthKbps(); - method public int getLinkUpstreamBandwidthKbps(); - method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier(); - method public int getOwnerUid(); - method public int getSignalStrength(); - method @Nullable public android.net.TransportInfo getTransportInfo(); - method public boolean hasCapability(int); - method public boolean hasTransport(int); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkCapabilities> CREATOR; - field public static final int NET_CAPABILITY_CAPTIVE_PORTAL = 17; // 0x11 - 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 - field public static final int NET_CAPABILITY_IMS = 4; // 0x4 - field public static final int NET_CAPABILITY_INTERNET = 12; // 0xc - field public static final int NET_CAPABILITY_MCX = 23; // 0x17 - field public static final int NET_CAPABILITY_MMS = 0; // 0x0 - field public static final int NET_CAPABILITY_NOT_CONGESTED = 20; // 0x14 - field public static final int NET_CAPABILITY_NOT_METERED = 11; // 0xb - field public static final int NET_CAPABILITY_NOT_RESTRICTED = 13; // 0xd - field public static final int NET_CAPABILITY_NOT_ROAMING = 18; // 0x12 - field public static final int NET_CAPABILITY_NOT_SUSPENDED = 21; // 0x15 - field public static final int NET_CAPABILITY_NOT_VPN = 15; // 0xf - field public static final int NET_CAPABILITY_RCS = 8; // 0x8 - field public static final int NET_CAPABILITY_SUPL = 1; // 0x1 - field public static final int NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25; // 0x19 - field public static final int NET_CAPABILITY_TRUSTED = 14; // 0xe - field public static final int NET_CAPABILITY_VALIDATED = 16; // 0x10 - field public static final int NET_CAPABILITY_WIFI_P2P = 6; // 0x6 - field public static final int NET_CAPABILITY_XCAP = 9; // 0x9 - field public static final int SIGNAL_STRENGTH_UNSPECIFIED = -2147483648; // 0x80000000 - field public static final int TRANSPORT_BLUETOOTH = 2; // 0x2 - field public static final int TRANSPORT_CELLULAR = 0; // 0x0 - field public static final int TRANSPORT_ETHERNET = 3; // 0x3 - field public static final int TRANSPORT_LOWPAN = 6; // 0x6 - field public static final int TRANSPORT_VPN = 4; // 0x4 - field public static final int TRANSPORT_WIFI = 1; // 0x1 - field public static final int TRANSPORT_WIFI_AWARE = 5; // 0x5 - } - - @Deprecated public class NetworkInfo implements android.os.Parcelable { - ctor @Deprecated public NetworkInfo(int, int, @Nullable String, @Nullable String); - method @Deprecated public int describeContents(); - method @Deprecated @NonNull public android.net.NetworkInfo.DetailedState getDetailedState(); - method @Deprecated public String getExtraInfo(); - method @Deprecated public String getReason(); - method @Deprecated public android.net.NetworkInfo.State getState(); - method @Deprecated public int getSubtype(); - method @Deprecated public String getSubtypeName(); - method @Deprecated public int getType(); - method @Deprecated public String getTypeName(); - method @Deprecated public boolean isAvailable(); - method @Deprecated public boolean isConnected(); - method @Deprecated public boolean isConnectedOrConnecting(); - method @Deprecated public boolean isFailover(); - method @Deprecated public boolean isRoaming(); - method @Deprecated public void setDetailedState(@NonNull android.net.NetworkInfo.DetailedState, @Nullable String, @Nullable String); - method @Deprecated public void writeToParcel(android.os.Parcel, int); - field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkInfo> CREATOR; - } - - @Deprecated public enum NetworkInfo.DetailedState { - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState AUTHENTICATING; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState BLOCKED; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CAPTIVE_PORTAL_CHECK; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CONNECTED; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CONNECTING; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState DISCONNECTED; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState DISCONNECTING; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState FAILED; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState IDLE; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState OBTAINING_IPADDR; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState SCANNING; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState SUSPENDED; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState VERIFYING_POOR_LINK; - } - - @Deprecated public enum NetworkInfo.State { - enum_constant @Deprecated public static final android.net.NetworkInfo.State CONNECTED; - enum_constant @Deprecated public static final android.net.NetworkInfo.State CONNECTING; - enum_constant @Deprecated public static final android.net.NetworkInfo.State DISCONNECTED; - enum_constant @Deprecated public static final android.net.NetworkInfo.State DISCONNECTING; - enum_constant @Deprecated public static final android.net.NetworkInfo.State SUSPENDED; - enum_constant @Deprecated public static final android.net.NetworkInfo.State UNKNOWN; - } - - public class NetworkRequest implements android.os.Parcelable { - method public boolean canBeSatisfiedBy(@Nullable android.net.NetworkCapabilities); - method public int describeContents(); - method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier(); - method public boolean hasCapability(int); - method public boolean hasTransport(int); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkRequest> CREATOR; - } - - public static class NetworkRequest.Builder { - ctor public NetworkRequest.Builder(); - method public android.net.NetworkRequest.Builder addCapability(int); - method public android.net.NetworkRequest.Builder addTransportType(int); - method public android.net.NetworkRequest build(); - method @NonNull public android.net.NetworkRequest.Builder clearCapabilities(); - method public android.net.NetworkRequest.Builder removeCapability(int); - method public android.net.NetworkRequest.Builder removeTransportType(int); - method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String); - method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier); - } - public abstract class NetworkSpecifier { ctor public NetworkSpecifier(); } @@ -26402,44 +26013,6 @@ package android.net { field public static final int TYPE_IKEV2_IPSEC_USER_PASS = 6; // 0x6 } - public final class Proxy { - ctor public Proxy(); - method @Deprecated public static String getDefaultHost(); - method @Deprecated public static int getDefaultPort(); - method @Deprecated public static String getHost(android.content.Context); - method @Deprecated public static int getPort(android.content.Context); - field @Deprecated public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO"; - field public static final String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE"; - } - - public class ProxyInfo implements android.os.Parcelable { - ctor public ProxyInfo(@Nullable android.net.ProxyInfo); - method public static android.net.ProxyInfo buildDirectProxy(String, int); - method public static android.net.ProxyInfo buildDirectProxy(String, int, java.util.List<java.lang.String>); - method public static android.net.ProxyInfo buildPacProxy(android.net.Uri); - method @NonNull public static android.net.ProxyInfo buildPacProxy(@NonNull android.net.Uri, int); - method public int describeContents(); - method public String[] getExclusionList(); - method public String getHost(); - method public android.net.Uri getPacFileUrl(); - method public int getPort(); - method public boolean isValid(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.ProxyInfo> CREATOR; - } - - public final class RouteInfo implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public android.net.IpPrefix getDestination(); - method @Nullable public java.net.InetAddress getGateway(); - method @Nullable public String getInterface(); - method public boolean hasGateway(); - method public boolean isDefaultRoute(); - method public boolean matches(java.net.InetAddress); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.RouteInfo> CREATOR; - } - @Deprecated public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory { ctor @Deprecated public SSLCertificateSocketFactory(int); method @Deprecated public java.net.Socket createSocket(java.net.Socket, String, int, boolean) throws java.io.IOException; @@ -26465,30 +26038,6 @@ package android.net { ctor public SSLSessionCache(android.content.Context); } - public abstract class SocketKeepalive implements java.lang.AutoCloseable { - method public final void close(); - method public final void start(@IntRange(from=0xa, to=0xe10) int); - method public final void stop(); - field public static final int ERROR_HARDWARE_ERROR = -31; // 0xffffffe1 - field public static final int ERROR_INSUFFICIENT_RESOURCES = -32; // 0xffffffe0 - field public static final int ERROR_INVALID_INTERVAL = -24; // 0xffffffe8 - field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb - field public static final int ERROR_INVALID_LENGTH = -23; // 0xffffffe9 - field public static final int ERROR_INVALID_NETWORK = -20; // 0xffffffec - field public static final int ERROR_INVALID_PORT = -22; // 0xffffffea - field public static final int ERROR_INVALID_SOCKET = -25; // 0xffffffe7 - field public static final int ERROR_SOCKET_NOT_IDLE = -26; // 0xffffffe6 - field public static final int ERROR_UNSUPPORTED = -30; // 0xffffffe2 - } - - public static class SocketKeepalive.Callback { - ctor public SocketKeepalive.Callback(); - method public void onDataReceived(); - method public void onError(int); - method public void onStarted(); - method public void onStopped(); - } - public final class TelephonyNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { method public int describeContents(); method public int getSubscriptionId(); @@ -26546,9 +26095,6 @@ package android.net { field public static final int UNSUPPORTED = -1; // 0xffffffff } - public interface TransportInfo { - } - public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable { method public abstract android.net.Uri.Builder buildUpon(); method public int compareTo(android.net.Uri); @@ -31921,10 +31467,10 @@ package android.os { method public static android.content.Intent createUserCreationIntent(@Nullable String, @Nullable String, @Nullable String, @Nullable android.os.PersistableBundle); method @WorkerThread public android.os.Bundle getApplicationRestrictions(String); method public long getSerialNumberForUser(android.os.UserHandle); - method @RequiresPermission("android.permission.MANAGE_USERS") public int getUserCount(); + method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.CREATE_USERS"}) public int getUserCount(); method public long getUserCreationTime(android.os.UserHandle); method public android.os.UserHandle getUserForSerialNumber(long); - method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}, conditional=true) public String getUserName(); + method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED, "android.permission.CREATE_USERS"}, conditional=true) public String getUserName(); method public java.util.List<android.os.UserHandle> getUserProfiles(); method public android.os.Bundle getUserRestrictions(); method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle); @@ -37163,8 +36709,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"; @@ -40871,6 +40419,7 @@ package android.telephony { } public static final class CarrierConfigManager.Apn { + field @Deprecated public static final String KEY_PREFIX = "apn."; field public static final String KEY_SETTINGS_DEFAULT_PROTOCOL_STRING = "apn.settings_default_protocol_string"; field public static final String KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING = "apn.settings_default_roaming_protocol_string"; field public static final String PROTOCOL_IPV4 = "IP"; @@ -48024,16 +47573,47 @@ package android.view { method public void onScaleEnd(android.view.ScaleGestureDetector); } + @UiThread public interface ScrollCaptureCallback { + method public void onScrollCaptureEnd(@NonNull Runnable); + method public void onScrollCaptureImageRequest(@NonNull android.view.ScrollCaptureSession, @NonNull android.os.CancellationSignal, @NonNull android.graphics.Rect, @NonNull java.util.function.Consumer<android.graphics.Rect>); + method public void onScrollCaptureSearch(@NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.graphics.Rect>); + method public void onScrollCaptureStart(@NonNull android.view.ScrollCaptureSession, @NonNull android.os.CancellationSignal, @NonNull Runnable); + } + + public class ScrollCaptureSession { + ctor public ScrollCaptureSession(@NonNull android.view.Surface, @NonNull android.graphics.Rect, @NonNull android.graphics.Point); + method @NonNull public android.graphics.Point getPositionInWindow(); + method @NonNull public android.graphics.Rect getScrollBounds(); + method @NonNull public android.view.Surface getSurface(); + } + + public final class ScrollCaptureTarget { + ctor public ScrollCaptureTarget(@NonNull android.view.View, @NonNull android.graphics.Rect, @NonNull android.graphics.Point, @NonNull android.view.ScrollCaptureCallback); + method @NonNull public android.view.ScrollCaptureCallback getCallback(); + method @NonNull public android.view.View getContainingView(); + method public int getHint(); + method @NonNull public android.graphics.Rect getLocalVisibleRect(); + method @NonNull public android.graphics.Point getPositionInWindow(); + method @Nullable public android.graphics.Rect getScrollBounds(); + method public void setScrollBounds(@Nullable android.graphics.Rect); + method @UiThread public void updatePositionInWindow(); + } + public class SearchEvent { ctor public SearchEvent(android.view.InputDevice); method public android.view.InputDevice getInputDevice(); } public class SoundEffectConstants { + method public static int getConstantForFocusDirection(int, boolean); method public static int getContantForFocusDirection(int); field public static final int CLICK = 0; // 0x0 field public static final int NAVIGATION_DOWN = 4; // 0x4 field public static final int NAVIGATION_LEFT = 1; // 0x1 + field public static final int NAVIGATION_REPEAT_DOWN = 8; // 0x8 + field public static final int NAVIGATION_REPEAT_LEFT = 5; // 0x5 + field public static final int NAVIGATION_REPEAT_RIGHT = 7; // 0x7 + field public static final int NAVIGATION_REPEAT_UP = 6; // 0x6 field public static final int NAVIGATION_RIGHT = 3; // 0x3 field public static final int NAVIGATION_UP = 2; // 0x2 } @@ -48345,6 +47925,7 @@ package android.view { method public void dispatchProvideStructure(android.view.ViewStructure); method protected void dispatchRestoreInstanceState(android.util.SparseArray<android.os.Parcelable>); method protected void dispatchSaveInstanceState(android.util.SparseArray<android.os.Parcelable>); + method public void dispatchScrollCaptureSearch(@NonNull android.graphics.Rect, @NonNull android.graphics.Point, @NonNull java.util.function.Consumer<android.view.ScrollCaptureTarget>); method protected void dispatchSetActivated(boolean); method protected void dispatchSetPressed(boolean); method protected void dispatchSetSelected(boolean); @@ -48504,6 +48085,7 @@ package android.view { method public int getScrollBarFadeDuration(); method public int getScrollBarSize(); method public int getScrollBarStyle(); + method public int getScrollCaptureHint(); method public int getScrollIndicators(); method public final int getScrollX(); method public final int getScrollY(); @@ -48672,6 +48254,7 @@ package android.view { method public void onRtlPropertiesChanged(int); method @CallSuper @Nullable protected android.os.Parcelable onSaveInstanceState(); method public void onScreenStateChanged(int); + method public void onScrollCaptureSearch(@NonNull android.graphics.Rect, @NonNull android.graphics.Point, @NonNull java.util.function.Consumer<android.view.ScrollCaptureTarget>); method protected void onScrollChanged(int, int, int, int); method protected boolean onSetAlpha(int); method protected void onSizeChanged(int, int, int, int); @@ -48854,6 +48437,8 @@ package android.view { method public void setScrollBarFadeDuration(int); method public void setScrollBarSize(int); method public void setScrollBarStyle(int); + method public final void setScrollCaptureCallback(@Nullable android.view.ScrollCaptureCallback); + method public void setScrollCaptureHint(int); method public void setScrollContainer(boolean); method public void setScrollIndicators(int); method public void setScrollIndicators(int, int); @@ -49032,6 +48617,10 @@ package android.view { field public static final int SCROLL_AXIS_HORIZONTAL = 1; // 0x1 field public static final int SCROLL_AXIS_NONE = 0; // 0x0 field public static final int SCROLL_AXIS_VERTICAL = 2; // 0x2 + field public static final int SCROLL_CAPTURE_HINT_AUTO = 0; // 0x0 + field public static final int SCROLL_CAPTURE_HINT_EXCLUDE = 1; // 0x1 + field public static final int SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS = 4; // 0x4 + field public static final int SCROLL_CAPTURE_HINT_INCLUDE = 2; // 0x2 field public static final int SCROLL_INDICATOR_BOTTOM = 2; // 0x2 field public static final int SCROLL_INDICATOR_END = 32; // 0x20 field public static final int SCROLL_INDICATOR_LEFT = 4; // 0x4 @@ -49816,6 +49405,7 @@ package android.view { method public abstract boolean performContextMenuIdentifierAction(int, int); method public abstract boolean performPanelIdentifierAction(int, int, int); method public abstract boolean performPanelShortcut(int, int, android.view.KeyEvent, int); + method public void registerScrollCaptureCallback(@NonNull android.view.ScrollCaptureCallback); method public final void removeOnFrameMetricsAvailableListener(android.view.Window.OnFrameMetricsAvailableListener); method public boolean requestFeature(int); method @NonNull public final <T extends android.view.View> T requireViewById(@IdRes int); @@ -49895,6 +49485,7 @@ package android.view { method public abstract void takeKeyEvents(boolean); method public abstract void takeSurface(android.view.SurfaceHolder.Callback2); method public abstract void togglePanel(int, android.view.KeyEvent); + method public void unregisterScrollCaptureCallback(@NonNull android.view.ScrollCaptureCallback); field public static final int DECOR_CAPTION_SHADE_AUTO = 0; // 0x0 field public static final int DECOR_CAPTION_SHADE_DARK = 2; // 0x2 field public static final int DECOR_CAPTION_SHADE_LIGHT = 1; // 0x1 diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index dd9582fddd4a..e6f0e4804655 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -163,67 +163,14 @@ package android.media.session { package android.net { - public final class ConnectivityFrameworkInitializer { - method public static void registerServiceWrappers(); - } - - public class ConnectivityManager { - method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); - method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @Nullable android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback); - method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle); - } - public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable { method public int getResourceId(); } - public final class NetworkAgentConfig implements android.os.Parcelable { - method @Nullable public String getSubscriberId(); - } - - public static final class NetworkAgentConfig.Builder { - method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String); - } - - public final class NetworkCapabilities implements android.os.Parcelable { - 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); - } - - public final class TcpRepairWindow { - ctor public TcpRepairWindow(int, int, int, int, int, int); - field public final int maxWindow; - field public final int rcvWnd; - field public final int rcvWndScale; - field public final int rcvWup; - field public final int sndWl1; - field public final int sndWnd; - } - - public final class TestNetworkInterface implements android.os.Parcelable { - ctor public TestNetworkInterface(@NonNull android.os.ParcelFileDescriptor, @NonNull String); - method public int describeContents(); - method @NonNull public android.os.ParcelFileDescriptor getFileDescriptor(); - method @NonNull public String getInterfaceName(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.TestNetworkInterface> CREATOR; - } - - public class TestNetworkManager { - method @NonNull public android.net.TestNetworkInterface createTapInterface(); - method @NonNull public android.net.TestNetworkInterface createTunInterface(@NonNull java.util.Collection<android.net.LinkAddress>); - method public void setupTestNetwork(@NonNull String, @NonNull android.os.IBinder); - method public void teardownTestNetwork(@NonNull android.net.Network); - field public static final String TEST_TAP_PREFIX = "testtap"; - } - public final class UnderlyingNetworkInfo implements android.os.Parcelable { ctor public UnderlyingNetworkInfo(int, @NonNull String, @NonNull java.util.List<java.lang.String>); method public int describeContents(); @@ -234,14 +181,6 @@ package android.net { field @NonNull public final java.util.List<java.lang.String> underlyingIfaces; } - public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo { - ctor public VpnTransportInfo(int); - method public int describeContents(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.VpnTransportInfo> CREATOR; - field public final int type; - } - } package android.os { diff --git a/core/api/removed.txt b/core/api/removed.txt index 990388a54c85..a99178d51d77 100644 --- a/core/api/removed.txt +++ b/core/api/removed.txt @@ -241,12 +241,6 @@ package android.media.tv { package android.net { - public class ConnectivityManager { - method @Deprecated public boolean requestRouteToHost(int, int); - method @Deprecated public int startUsingNetworkFeature(int, String); - method @Deprecated public int stopUsingNetworkFeature(int, String); - } - @Deprecated public class NetworkBadging { method @NonNull public static android.graphics.drawable.Drawable getWifiIcon(@IntRange(from=0, to=4) int, int, @Nullable android.content.res.Resources.Theme); field public static final int BADGING_4K = 30; // 0x1e diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 17783b7ee0d0..b55eb99db44a 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -176,6 +176,7 @@ package android { field public static final String OBSERVE_APP_USAGE = "android.permission.OBSERVE_APP_USAGE"; field public static final String OBSERVE_NETWORK_POLICY = "android.permission.OBSERVE_NETWORK_POLICY"; field public static final String OBSERVE_ROLE_HOLDERS = "android.permission.OBSERVE_ROLE_HOLDERS"; + field public static final String OBSERVE_SENSOR_PRIVACY = "android.permission.OBSERVE_SENSOR_PRIVACY"; field public static final String OPEN_ACCESSIBILITY_DETAILS_SETTINGS = "android.permission.OPEN_ACCESSIBILITY_DETAILS_SETTINGS"; field public static final String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG"; field public static final String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT"; @@ -352,6 +353,7 @@ package android { field public static final int config_systemContacts = 17039403; // 0x104002b field public static final int config_systemGallery = 17039399; // 0x1040027 field public static final int config_systemShell = 17039402; // 0x104002a + field public static final int config_systemSpeechRecognizer = 17039406; // 0x104002e } public static final class R.style { @@ -641,8 +643,9 @@ package android.app { method public static android.app.BroadcastOptions makeBasic(); method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean); method public void setDontSendToRestrictedApps(boolean); - method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(long); - method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(int, long); + method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppAllowlist(long, int, int, @Nullable String); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(long); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(int, long); method public android.os.Bundle toBundle(); } @@ -884,7 +887,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(); @@ -2461,10 +2464,10 @@ package android.content.pm { } public static class PackageInstaller.Session implements java.io.Closeable { - method @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public void addFile(int, @NonNull String, long, @NonNull byte[], @Nullable byte[]); + method public void addFile(int, @NonNull String, long, @NonNull byte[], @Nullable byte[]); method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void commitTransferred(@NonNull android.content.IntentSender); - method @Nullable @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public android.content.pm.DataLoaderParams getDataLoaderParams(); - method @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public void removeFile(int, @NonNull String); + method @Nullable public android.content.pm.DataLoaderParams getDataLoaderParams(); + method public void removeFile(int, @NonNull String); } public static class PackageInstaller.SessionInfo implements android.os.Parcelable { @@ -2485,7 +2488,7 @@ package android.content.pm { public static class PackageInstaller.SessionParams implements android.os.Parcelable { method @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) public void setAllocateAggressive(boolean); method @Deprecated public void setAllowDowngrade(boolean); - method @RequiresPermission(allOf={android.Manifest.permission.INSTALL_PACKAGES, "com.android.permission.USE_INSTALLER_V2"}) public void setDataLoaderParams(@NonNull android.content.pm.DataLoaderParams); + method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setDataLoaderParams(@NonNull android.content.pm.DataLoaderParams); method public void setDontKillApp(boolean); method public void setEnableRollback(boolean); method public void setEnableRollback(boolean, int); @@ -2550,10 +2553,12 @@ package android.content.pm { field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES"; field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS"; field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio"; + field public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle"; field public static final String FEATURE_CONTEXT_HUB = "android.hardware.context_hub"; field public static final String FEATURE_CROSS_LAYER_BLUR = "android.software.cross_layer_blur"; field @Deprecated public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery"; field public static final String FEATURE_INCREMENTAL_DELIVERY_VERSION = "android.software.incremental_delivery_version"; + field public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle"; field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow"; field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock"; field public static final String FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION = "android.hardware.telephony.ims.singlereg"; @@ -2874,7 +2879,7 @@ package android.graphics.fonts { } public class FontManager { - method @Nullable public android.text.FontConfig getFontConfig(); + method @NonNull public android.text.FontConfig getFontConfig(); method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFamily(@NonNull android.graphics.fonts.FontFamilyUpdateRequest, @IntRange(from=0) int); method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFile(@NonNull android.graphics.fonts.FontFileUpdateRequest, @IntRange(from=0) int); method @Deprecated @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFile(@NonNull android.os.ParcelFileDescriptor, @NonNull byte[], @IntRange(from=0) int); @@ -2908,6 +2913,22 @@ package android.hardware { method public boolean injectSensorData(android.hardware.Sensor, float[], int, long); } + public final class SensorPrivacyManager { + method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener); + method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener); + method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int); + method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener); + } + + public static interface SensorPrivacyManager.OnSensorPrivacyChangedListener { + method public void onSensorPrivacyChanged(boolean); + } + + public static class SensorPrivacyManager.Sensors { + field public static final int CAMERA = 2; // 0x2 + field public static final int MICROPHONE = 1; // 0x1 + } + } package android.hardware.biometrics { @@ -7063,102 +7084,6 @@ package android.metrics { package android.net { - public class CaptivePortal implements android.os.Parcelable { - method public void logEvent(int, @NonNull String); - method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void reevaluateNetwork(); - method public void useNetwork(); - field public static final int APP_REQUEST_REEVALUATION_REQUIRED = 100; // 0x64 - field public static final int APP_RETURN_DISMISSED = 0; // 0x0 - field public static final int APP_RETURN_UNWANTED = 1; // 0x1 - field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2 - } - - public final class CaptivePortalData implements android.os.Parcelable { - method public int describeContents(); - method public long getByteLimit(); - method public long getExpiryTimeMillis(); - method public long getRefreshTimeMillis(); - method @Nullable public android.net.Uri getUserPortalUrl(); - method public int getUserPortalUrlSource(); - method @Nullable public String getVenueFriendlyName(); - method @Nullable public android.net.Uri getVenueInfoUrl(); - method public int getVenueInfoUrlSource(); - method public boolean isCaptive(); - method public boolean isSessionExtendable(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field public static final int CAPTIVE_PORTAL_DATA_SOURCE_OTHER = 0; // 0x0 - field public static final int CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT = 1; // 0x1 - field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortalData> CREATOR; - } - - public static class CaptivePortalData.Builder { - ctor public CaptivePortalData.Builder(); - ctor public CaptivePortalData.Builder(@Nullable android.net.CaptivePortalData); - method @NonNull public android.net.CaptivePortalData build(); - method @NonNull public android.net.CaptivePortalData.Builder setBytesRemaining(long); - method @NonNull public android.net.CaptivePortalData.Builder setCaptive(boolean); - method @NonNull public android.net.CaptivePortalData.Builder setExpiryTime(long); - method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long); - method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean); - method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri); - method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri, int); - method @NonNull public android.net.CaptivePortalData.Builder setVenueFriendlyName(@Nullable String); - method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri); - method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri, int); - } - - public class ConnectivityManager { - method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull android.os.ParcelFileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback); - method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull java.net.Socket, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback); - method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCaptivePortalServerUrl(); - method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener); - method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported(); - method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public int registerNetworkProvider(@NonNull android.net.NetworkProvider); - method public void registerQosCallback(@NonNull android.net.QosSocketInfo, @NonNull android.net.QosCallback, @NonNull java.util.concurrent.Executor); - method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback); - method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void requestNetwork(@NonNull android.net.NetworkRequest, int, int, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback); - method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean); - method @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE) public void setOemNetworkPreference(@NonNull android.net.OemNetworkPreferences, @Nullable java.util.concurrent.Executor, @Nullable android.net.ConnectivityManager.OnSetOemNetworkPreferenceListener); - method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi(); - method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle); - method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback); - method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler); - method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int); - method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider); - method public void unregisterQosCallback(@NonNull android.net.QosCallback); - method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback); - field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC"; - field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT"; - field public static final int TETHERING_BLUETOOTH = 2; // 0x2 - field public static final int TETHERING_USB = 1; // 0x1 - field public static final int TETHERING_WIFI = 0; // 0x0 - field @Deprecated public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; // 0xd - field @Deprecated public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 - field @Deprecated public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb - field public static final int TYPE_NONE = -1; // 0xffffffff - field @Deprecated public static final int TYPE_PROXY = 16; // 0x10 - field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd - } - - public static interface ConnectivityManager.OnSetOemNetworkPreferenceListener { - method public void onComplete(); - } - - @Deprecated public abstract static class ConnectivityManager.OnStartTetheringCallback { - ctor @Deprecated public ConnectivityManager.OnStartTetheringCallback(); - method @Deprecated public void onTetheringFailed(); - method @Deprecated public void onTetheringStarted(); - } - - @Deprecated public static interface ConnectivityManager.OnTetheringEntitlementResultListener { - method @Deprecated public void onTetheringEntitlementResult(int); - } - - @Deprecated public abstract static class ConnectivityManager.OnTetheringEventCallback { - ctor @Deprecated public ConnectivityManager.OnTetheringEventCallback(); - method @Deprecated public void onUpstreamChanged(@Nullable android.net.Network); - } - public class DnsResolverServiceManager { method @NonNull @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public static android.os.IBinder getService(@NonNull android.content.Context); } @@ -7176,48 +7101,6 @@ package android.net { method public void release(); } - public final class InvalidPacketException extends java.lang.Exception { - ctor public InvalidPacketException(int); - method public int getError(); - field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb - field public static final int ERROR_INVALID_LENGTH = -23; // 0xffffffe9 - field public static final int ERROR_INVALID_PORT = -22; // 0xffffffea - } - - public final class IpConfiguration implements android.os.Parcelable { - ctor public IpConfiguration(); - ctor public IpConfiguration(@NonNull android.net.IpConfiguration); - method public int describeContents(); - method @Nullable public android.net.ProxyInfo getHttpProxy(); - method @NonNull public android.net.IpConfiguration.IpAssignment getIpAssignment(); - method @NonNull public android.net.IpConfiguration.ProxySettings getProxySettings(); - method @Nullable public android.net.StaticIpConfiguration getStaticIpConfiguration(); - method public void setHttpProxy(@Nullable android.net.ProxyInfo); - method public void setIpAssignment(@NonNull android.net.IpConfiguration.IpAssignment); - method public void setProxySettings(@NonNull android.net.IpConfiguration.ProxySettings); - method public void setStaticIpConfiguration(@Nullable android.net.StaticIpConfiguration); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.IpConfiguration> CREATOR; - } - - public enum IpConfiguration.IpAssignment { - enum_constant public static final android.net.IpConfiguration.IpAssignment DHCP; - enum_constant public static final android.net.IpConfiguration.IpAssignment STATIC; - enum_constant public static final android.net.IpConfiguration.IpAssignment UNASSIGNED; - } - - public enum IpConfiguration.ProxySettings { - enum_constant public static final android.net.IpConfiguration.ProxySettings NONE; - enum_constant public static final android.net.IpConfiguration.ProxySettings PAC; - enum_constant public static final android.net.IpConfiguration.ProxySettings STATIC; - enum_constant public static final android.net.IpConfiguration.ProxySettings UNASSIGNED; - } - - public final class IpPrefix implements android.os.Parcelable { - ctor public IpPrefix(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int); - ctor public IpPrefix(@NonNull String); - } - public final class IpSecManager { method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException; method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; @@ -7235,68 +7118,6 @@ package android.net { method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecTransform buildTunnelModeTransform(@NonNull java.net.InetAddress, @NonNull android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; } - public class KeepalivePacketData { - ctor protected KeepalivePacketData(@NonNull java.net.InetAddress, @IntRange(from=0, to=65535) int, @NonNull java.net.InetAddress, @IntRange(from=0, to=65535) int, @NonNull byte[]) throws android.net.InvalidPacketException; - method @NonNull public java.net.InetAddress getDstAddress(); - method public int getDstPort(); - method @NonNull public byte[] getPacket(); - method @NonNull public java.net.InetAddress getSrcAddress(); - method public int getSrcPort(); - } - - public class LinkAddress implements android.os.Parcelable { - ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int); - ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int, long, long); - ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int); - ctor public LinkAddress(@NonNull String); - ctor public LinkAddress(@NonNull String, int, int); - method public long getDeprecationTime(); - method public long getExpirationTime(); - method public boolean isGlobalPreferred(); - method public boolean isIpv4(); - method public boolean isIpv6(); - method public boolean isSameAddressAs(@Nullable android.net.LinkAddress); - field public static final long LIFETIME_PERMANENT = 9223372036854775807L; // 0x7fffffffffffffffL - field public static final long LIFETIME_UNKNOWN = -1L; // 0xffffffffffffffffL - } - - public final class LinkProperties implements android.os.Parcelable { - ctor public LinkProperties(@Nullable android.net.LinkProperties); - ctor public LinkProperties(@Nullable android.net.LinkProperties, boolean); - method public boolean addDnsServer(@NonNull java.net.InetAddress); - method public boolean addLinkAddress(@NonNull android.net.LinkAddress); - method public boolean addPcscfServer(@NonNull java.net.InetAddress); - method @NonNull public java.util.List<java.net.InetAddress> getAddresses(); - method @NonNull public java.util.List<java.lang.String> getAllInterfaceNames(); - method @NonNull public java.util.List<android.net.LinkAddress> getAllLinkAddresses(); - method @NonNull public java.util.List<android.net.RouteInfo> getAllRoutes(); - method @Nullable public android.net.Uri getCaptivePortalApiUrl(); - method @Nullable public android.net.CaptivePortalData getCaptivePortalData(); - method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers(); - method @Nullable public String getTcpBufferSizes(); - method @NonNull public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers(); - method public boolean hasGlobalIpv6Address(); - method public boolean hasIpv4Address(); - method public boolean hasIpv4DefaultRoute(); - method public boolean hasIpv4DnsServer(); - method public boolean hasIpv6DefaultRoute(); - method public boolean hasIpv6DnsServer(); - method public boolean isIpv4Provisioned(); - method public boolean isIpv6Provisioned(); - method public boolean isProvisioned(); - method public boolean isReachable(@NonNull java.net.InetAddress); - method public boolean removeDnsServer(@NonNull java.net.InetAddress); - method public boolean removeLinkAddress(@NonNull android.net.LinkAddress); - method public boolean removeRoute(@NonNull android.net.RouteInfo); - method public void setCaptivePortalApiUrl(@Nullable android.net.Uri); - method public void setCaptivePortalData(@Nullable android.net.CaptivePortalData); - method public void setPcscfServers(@NonNull java.util.Collection<java.net.InetAddress>); - method public void setPrivateDnsServerName(@Nullable String); - method public void setTcpBufferSizes(@Nullable String); - method public void setUsePrivateDns(boolean); - method public void setValidatedPrivateDnsServers(@NonNull java.util.Collection<java.net.InetAddress>); - } - public final class MatchAllNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { ctor public MatchAllNetworkSpecifier(); method public int describeContents(); @@ -7304,104 +7125,6 @@ package android.net { field @NonNull public static final android.os.Parcelable.Creator<android.net.MatchAllNetworkSpecifier> CREATOR; } - public final class NattKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable { - ctor public NattKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[]) throws android.net.InvalidPacketException; - method public int describeContents(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.NattKeepalivePacketData> CREATOR; - } - - public class Network implements android.os.Parcelable { - ctor public Network(@NonNull android.net.Network); - method public int getNetId(); - method @NonNull public android.net.Network getPrivateDnsBypassingCopy(); - } - - public abstract class NetworkAgent { - ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, int, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider); - method @Nullable public android.net.Network getNetwork(); - method public void markConnected(); - method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData); - method public void onAutomaticReconnectDisabled(); - method public void onNetworkUnwanted(); - method public void onQosCallbackRegistered(int, @NonNull android.net.QosFilter); - method public void onQosCallbackUnregistered(int); - method public void onRemoveKeepalivePacketFilter(int); - method public void onSaveAcceptUnvalidated(boolean); - method public void onSignalStrengthThresholdsUpdated(@NonNull int[]); - method public void onStartSocketKeepalive(int, @NonNull java.time.Duration, @NonNull android.net.KeepalivePacketData); - method public void onStopSocketKeepalive(int); - method public void onValidationStatus(int, @Nullable android.net.Uri); - method @NonNull public android.net.Network register(); - method public final void sendLinkProperties(@NonNull android.net.LinkProperties); - method public final void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities); - method public final void sendNetworkScore(@IntRange(from=0, to=99) int); - method public final void sendQosCallbackError(int, int); - method public final void sendQosSessionAvailable(int, int, @NonNull android.telephony.data.EpsBearerQosSessionAttributes); - method public final void sendQosSessionLost(int, int); - method public final void sendSocketKeepaliveEvent(int, int); - method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>); - method public void unregister(); - field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2 - field public static final int VALIDATION_STATUS_VALID = 1; // 0x1 - } - - public final class NetworkAgentConfig implements android.os.Parcelable { - method public int describeContents(); - method public int getLegacyType(); - method @NonNull public String getLegacyTypeName(); - method public boolean isExplicitlySelected(); - method public boolean isPartialConnectivityAcceptable(); - method public boolean isUnvalidatedConnectivityAcceptable(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkAgentConfig> CREATOR; - } - - public static final class NetworkAgentConfig.Builder { - ctor public NetworkAgentConfig.Builder(); - method @NonNull public android.net.NetworkAgentConfig build(); - method @NonNull public android.net.NetworkAgentConfig.Builder setExplicitlySelected(boolean); - method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyType(int); - method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyTypeName(@NonNull String); - method @NonNull public android.net.NetworkAgentConfig.Builder setPartialConnectivityAcceptable(boolean); - method @NonNull public android.net.NetworkAgentConfig.Builder setUnvalidatedConnectivityAcceptable(boolean); - } - - public final class NetworkCapabilities implements android.os.Parcelable { - ctor public NetworkCapabilities(@Nullable android.net.NetworkCapabilities, boolean); - method @NonNull public int[] getAdministratorUids(); - method @Nullable public String getSsid(); - method @NonNull public int[] getTransportTypes(); - method public boolean isPrivateDnsBroken(); - method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities); - field public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; // 0x1c - field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16 - field public static final int NET_CAPABILITY_OEM_PRIVATE = 26; // 0x1a - field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18 - field public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27; // 0x1b - } - - public static final class NetworkCapabilities.Builder { - ctor public NetworkCapabilities.Builder(); - ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities); - method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int); - method @NonNull public android.net.NetworkCapabilities.Builder addTransportType(int); - method @NonNull public android.net.NetworkCapabilities build(); - method @NonNull public android.net.NetworkCapabilities.Builder clearAll(); - method @NonNull public android.net.NetworkCapabilities.Builder removeCapability(int); - method @NonNull public android.net.NetworkCapabilities.Builder removeTransportType(int); - method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setAdministratorUids(@NonNull int[]); - method @NonNull public android.net.NetworkCapabilities.Builder setLinkDownstreamBandwidthKbps(int); - method @NonNull public android.net.NetworkCapabilities.Builder setLinkUpstreamBandwidthKbps(int); - method @NonNull public android.net.NetworkCapabilities.Builder setNetworkSpecifier(@Nullable android.net.NetworkSpecifier); - method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setOwnerUid(int); - method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorPackageName(@Nullable String); - method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorUid(int); - method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkCapabilities.Builder setSignalStrength(int); - method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String); - method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo); - } - public class NetworkKey implements android.os.Parcelable { ctor public NetworkKey(android.net.WifiKey); method @Nullable public static android.net.NetworkKey createFromScanResult(@NonNull android.net.wifi.ScanResult); @@ -7413,15 +7136,6 @@ package android.net { field public final android.net.WifiKey wifiKey; } - public class NetworkProvider { - ctor public NetworkProvider(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String); - method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void declareNetworkRequestUnfulfillable(@NonNull android.net.NetworkRequest); - method public int getProviderId(); - method public void onNetworkRequestWithdrawn(@NonNull android.net.NetworkRequest); - method public void onNetworkRequested(@NonNull android.net.NetworkRequest, @IntRange(from=0, to=99) int, int); - field public static final int ID_NONE = -1; // 0xffffffff - } - public abstract class NetworkRecommendationProvider { ctor public NetworkRecommendationProvider(android.content.Context, java.util.concurrent.Executor); method public final android.os.IBinder getBinder(); @@ -7431,15 +7145,6 @@ package android.net { public class NetworkReleasedException extends java.lang.Exception { } - public class NetworkRequest implements android.os.Parcelable { - method @Nullable public String getRequestorPackageName(); - method public int getRequestorUid(); - } - - public static class NetworkRequest.Builder { - method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int); - } - public class NetworkScoreManager { method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public boolean clearScores() throws java.lang.SecurityException; method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public void disableScoring() throws java.lang.SecurityException; @@ -7564,16 +7269,6 @@ package android.net { field @NonNull public static final android.os.Parcelable.Creator<android.net.QosSocketInfo> CREATOR; } - public final class RouteInfo implements android.os.Parcelable { - ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int); - ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int, int); - method public int getMtu(); - method public int getType(); - field public static final int RTN_THROW = 9; // 0x9 - field public static final int RTN_UNICAST = 1; // 0x1 - field public static final int RTN_UNREACHABLE = 7; // 0x7 - } - public class RssiCurve implements android.os.Parcelable { ctor public RssiCurve(int, int, byte[]); ctor public RssiCurve(int, int, byte[], int); @@ -7605,53 +7300,12 @@ package android.net { field public final android.net.RssiCurve rssiCurve; } - public abstract class SocketKeepalive implements java.lang.AutoCloseable { - field public static final int SUCCESS = 0; // 0x0 - } - public class SocketLocalAddressChangedException extends java.lang.Exception { } public class SocketNotBoundException extends java.lang.Exception { } - public final class StaticIpConfiguration implements android.os.Parcelable { - ctor public StaticIpConfiguration(); - ctor public StaticIpConfiguration(@Nullable android.net.StaticIpConfiguration); - method public void addDnsServer(@NonNull java.net.InetAddress); - method public void clear(); - method public int describeContents(); - method @NonNull public java.util.List<java.net.InetAddress> getDnsServers(); - method @Nullable public String getDomains(); - method @Nullable public java.net.InetAddress getGateway(); - method @Nullable public android.net.LinkAddress getIpAddress(); - method @NonNull public java.util.List<android.net.RouteInfo> getRoutes(@Nullable String); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.StaticIpConfiguration> CREATOR; - } - - public static final class StaticIpConfiguration.Builder { - ctor public StaticIpConfiguration.Builder(); - method @NonNull public android.net.StaticIpConfiguration build(); - method @NonNull public android.net.StaticIpConfiguration.Builder setDnsServers(@NonNull Iterable<java.net.InetAddress>); - method @NonNull public android.net.StaticIpConfiguration.Builder setDomains(@Nullable String); - method @NonNull public android.net.StaticIpConfiguration.Builder setGateway(@Nullable java.net.InetAddress); - method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@Nullable android.net.LinkAddress); - } - - public final class TcpKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable { - ctor public TcpKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[], int, int, int, int, int, int) throws android.net.InvalidPacketException; - method public int describeContents(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.TcpKeepalivePacketData> CREATOR; - field public final int ipTos; - field public final int ipTtl; - field public final int tcpAck; - field public final int tcpSeq; - field public final int tcpWindow; - field public final int tcpWindowScale; - } - public class TrafficStats { method public static void setThreadStatsTagApp(); method public static void setThreadStatsTagBackup(); @@ -7664,11 +7318,6 @@ package android.net { field public static final int TAG_SYSTEM_IMPERSONATION_RANGE_START = -256; // 0xffffff00 } - public interface TransportInfo { - method public default boolean hasLocationSensitiveFields(); - method @NonNull public default android.net.TransportInfo makeCopy(boolean); - } - public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable { method @NonNull public String toSafeString(); } @@ -7692,23 +7341,6 @@ package android.net { } -package android.net.apf { - - public final class ApfCapabilities implements android.os.Parcelable { - ctor public ApfCapabilities(int, int, int); - method public int describeContents(); - method public static boolean getApfDrop8023Frames(); - method @NonNull public static int[] getApfEtherTypeBlackList(); - method public boolean hasDataAccess(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.net.apf.ApfCapabilities> CREATOR; - field public final int apfPacketFormat; - field public final int apfVersionSupported; - field public final int maximumApfProgramSize; - } - -} - package android.net.metrics { @Deprecated public final class ApfProgramEvent implements android.net.metrics.IpConnectivityLog.Event { @@ -7903,19 +7535,6 @@ package android.net.sip { } -package android.net.util { - - public final class SocketUtils { - method public static void bindSocketToInterface(@NonNull java.io.FileDescriptor, @NonNull String) throws android.system.ErrnoException; - method public static void closeSocket(@Nullable java.io.FileDescriptor) throws java.io.IOException; - method @NonNull public static java.net.SocketAddress makeNetlinkSocketAddress(int, int); - method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int); - method @Deprecated @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, @NonNull byte[]); - method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int, @NonNull byte[]); - } - -} - package android.net.vcn { public class VcnManager { @@ -8607,11 +8226,18 @@ package android.os { method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull String); method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull java.util.List<java.lang.String>); method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void removeFromWhitelist(@NonNull String); - method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long); - method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, @NonNull String); + method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long, int, @Nullable String); + method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long); + method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, @Nullable String); + method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, int, @Nullable String); field public static final int EVENT_MMS = 2; // 0x2 field public static final int EVENT_SMS = 1; // 0x1 field public static final int EVENT_UNSPECIFIED = 0; // 0x0 + field public static final int REASON_ACTIVITY_RECOGNITION = 102; // 0x66 + field public static final int REASON_GEOFENCING = 100; // 0x64 + field public static final int REASON_OTHER = 1; // 0x1 + field public static final int REASON_PUSH_MESSAGING = 101; // 0x65 + field public static final int REASON_UNKNOWN = 0; // 0x0 field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0 field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1 } @@ -8760,13 +8386,13 @@ package android.os { method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle createProfile(@NonNull String, @NonNull String, @NonNull java.util.Set<java.lang.String>) throws android.os.UserManager.UserOperationException; method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getAllProfiles(); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getEnabledProfiles(); - method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle); + method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle getRestrictedProfileParent(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountName(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.PersistableBundle getSeedAccountOptions(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountType(); - method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public long[] getSerialNumbersOfUsers(boolean); - method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserHandle> getUserHandles(boolean); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public long[] getSerialNumbersOfUsers(boolean); + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.os.UserHandle> getUserHandles(boolean); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public android.graphics.Bitmap getUserIcon(); method @Deprecated @android.os.UserManager.UserRestrictionSource @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int getUserRestrictionSource(String, android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(String, android.os.UserHandle); @@ -9204,6 +8830,7 @@ package android.provider { field public static final String NAMESPACE_BIOMETRICS = "biometrics"; field public static final String NAMESPACE_BLOBSTORE = "blobstore"; field public static final String NAMESPACE_BLUETOOTH = "bluetooth"; + field public static final String NAMESPACE_CLIPBOARD = "clipboard"; field public static final String NAMESPACE_CONNECTIVITY = "connectivity"; field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture"; field @Deprecated public static final String NAMESPACE_DEX_BOOT = "dex_boot"; diff --git a/core/api/test-current.txt b/core/api/test-current.txt index f0a2a492c3cf..28724f271a7f 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); @@ -106,7 +110,10 @@ package android.app { 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 @@ -704,8 +718,10 @@ package android.content.pm { method public void holdLock(android.os.IBinder, int); method @RequiresPermission(android.Manifest.permission.KEEP_UNINSTALLED_PACKAGES) public void setKeepUninstalledPackages(@NonNull java.util.List<java.lang.String>); field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage"; + field public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle"; field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption"; field public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec"; + field public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle"; field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80 field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000 field public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services"; @@ -732,6 +748,65 @@ package android.content.pm { ctor public ShortcutManager(android.content.Context); } + public class UserInfo implements android.os.Parcelable { + ctor public UserInfo(int, String, int); + ctor public UserInfo(int, String, String, int); + ctor public UserInfo(int, String, String, int, String); + ctor @Deprecated public UserInfo(); + ctor public UserInfo(android.content.pm.UserInfo); + method public boolean canHaveProfile(); + method public int describeContents(); + method public android.os.UserHandle getUserHandle(); + method public boolean isAdmin(); + method public boolean isDemo(); + method public boolean isEnabled(); + method public boolean isEphemeral(); + method public boolean isFull(); + method public boolean isGuest(); + method public boolean isInitialized(); + method public boolean isManagedProfile(); + method public boolean isPrimary(); + method public boolean isProfile(); + method public boolean isQuietModeEnabled(); + method public boolean isRestricted(); + method public boolean isSystemOnly(); + method public static boolean isSystemOnly(int); + method public boolean supportsSwitchTo(); + method public boolean supportsSwitchToByUser(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.UserInfo> CREATOR; + field public static final int FLAG_ADMIN = 2; // 0x2 + field @Deprecated public static final int FLAG_DEMO = 512; // 0x200 + field public static final int FLAG_DISABLED = 64; // 0x40 + field public static final int FLAG_EPHEMERAL = 256; // 0x100 + field public static final int FLAG_FULL = 1024; // 0x400 + field @Deprecated public static final int FLAG_GUEST = 4; // 0x4 + field public static final int FLAG_INITIALIZED = 16; // 0x10 + field @Deprecated public static final int FLAG_MANAGED_PROFILE = 32; // 0x20 + field public static final int FLAG_PRIMARY = 1; // 0x1 + field public static final int FLAG_PROFILE = 4096; // 0x1000 + field public static final int FLAG_QUIET_MODE = 128; // 0x80 + field @Deprecated public static final int FLAG_RESTRICTED = 8; // 0x8 + field public static final int FLAG_SYSTEM = 2048; // 0x800 + field public static final int NO_PROFILE_GROUP_ID = -10000; // 0xffffd8f0 + field public boolean convertedFromPreCreated; + field public long creationTime; + field public int flags; + field public boolean guestToRemove; + field public String iconPath; + field public int id; + field public String lastLoggedInFingerprint; + field public long lastLoggedInTime; + field public String name; + field public boolean partial; + field public boolean preCreated; + field public int profileBadge; + field public int profileGroupId; + field public int restrictedProfileParentId; + field public int serialNumber; + field public String userType; + } + } package android.content.res { @@ -845,7 +920,7 @@ package android.graphics.drawable { package android.graphics.fonts { public class FontManager { - method @Nullable public android.text.FontConfig getFontConfig(); + method @NonNull public android.text.FontConfig getFontConfig(); method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFamily(@NonNull android.graphics.fonts.FontFamilyUpdateRequest, @IntRange(from=0) int); method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFile(@NonNull android.graphics.fonts.FontFileUpdateRequest, @IntRange(from=0) int); method @Deprecated @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFile(@NonNull android.os.ParcelFileDescriptor, @NonNull byte[], @IntRange(from=0) int); @@ -866,11 +941,21 @@ package android.graphics.fonts { package android.hardware { public final class SensorPrivacyManager { - method public boolean isIndividualSensorPrivacyEnabled(int); - method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setIndividualSensorPrivacy(int, boolean); - method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setIndividualSensorPrivacyForProfileGroup(int, boolean); - field public static final int INDIVIDUAL_SENSOR_CAMERA = 2; // 0x2 - field public static final int INDIVIDUAL_SENSOR_MICROPHONE = 1; // 0x1 + method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener); + method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener); + method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int); + method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener); + method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacy(int, boolean); + method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacyForProfileGroup(int, boolean); + } + + public static interface SensorPrivacyManager.OnSensorPrivacyChangedListener { + method public void onSensorPrivacyChanged(boolean); + } + + public static class SensorPrivacyManager.Sensors { + field public static final int CAMERA = 2; // 0x2 + field public static final int MICROPHONE = 1; // 0x1 } } @@ -1485,8 +1570,15 @@ package android.os { } public class UserManager { + method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createProfileForUser(@Nullable String, @NonNull String, int, int, @Nullable String[]); + method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createRestrictedProfile(@Nullable String); + method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createUser(@Nullable String, @NonNull String, int); + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public String getUserType(); + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getUsers(boolean, boolean, boolean); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle); + method public static boolean isGuestUserEphemeral(); method public static boolean isSplitSystemUser(); + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo preCreateUser(@NonNull String) throws android.os.UserManager.UserOperationException; } public final class VibrationAttributes implements android.os.Parcelable { diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt index 87fb5b1b3b2e..a536efb2b488 100644 --- a/core/api/test-lint-baseline.txt +++ b/core/api/test-lint-baseline.txt @@ -468,7 +468,7 @@ GetterSetterNames: android.location.GnssMeasurement#setBasebandCn0DbHz(double): GetterSetterNames: android.location.GnssMeasurement#setCarrierFrequencyHz(float): GetterSetterNames: android.location.GnssMeasurement#setCodeType(String): - + GetterSetterNames: android.location.GnssMeasurement#setCorrelationVectors(java.util.Collection<android.location.CorrelationVector>): GetterSetterNames: android.location.GnssMeasurement#setFullInterSignalBiasNanos(double): @@ -480,7 +480,7 @@ GetterSetterNames: android.location.GnssMeasurement#setSatelliteInterSignalBiasN GetterSetterNames: android.location.GnssMeasurement#setSatelliteInterSignalBiasUncertaintyNanos(double): GetterSetterNames: android.location.GnssMeasurement#setSatellitePvt(android.location.SatellitePvt): - + GetterSetterNames: android.location.GnssMeasurement#setSnrInDb(double): GetterSetterNames: android.location.LocationRequest#isLocationSettingsIgnored(): @@ -953,6 +953,32 @@ MissingNullability: android.content.pm.PackageManager#holdLock(android.os.IBinde MissingNullability: android.content.pm.ShortcutManager#ShortcutManager(android.content.Context) parameter #0: +MissingNullability: android.content.pm.UserInfo#UserInfo(android.content.pm.UserInfo) parameter #0: + Missing nullability on parameter `orig` in method `UserInfo` +MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int) parameter #1: + Missing nullability on parameter `name` in method `UserInfo` +MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int) parameter #2: + Missing nullability on parameter `iconPath` in method `UserInfo` +MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int, String) parameter #1: + Missing nullability on parameter `name` in method `UserInfo` +MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int, String) parameter #2: + Missing nullability on parameter `iconPath` in method `UserInfo` +MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int, String) parameter #4: + Missing nullability on parameter `userType` in method `UserInfo` +MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, int) parameter #1: + Missing nullability on parameter `name` in method `UserInfo` +MissingNullability: android.content.pm.UserInfo#getUserHandle(): + Missing nullability on method `getUserHandle` return +MissingNullability: android.content.pm.UserInfo#iconPath: + Missing nullability on field `iconPath` in class `class android.content.pm.UserInfo` +MissingNullability: android.content.pm.UserInfo#lastLoggedInFingerprint: + Missing nullability on field `lastLoggedInFingerprint` in class `class android.content.pm.UserInfo` +MissingNullability: android.content.pm.UserInfo#name: + Missing nullability on field `name` in class `class android.content.pm.UserInfo` +MissingNullability: android.content.pm.UserInfo#userType: + Missing nullability on field `userType` in class `class android.content.pm.UserInfo` +MissingNullability: android.content.pm.UserInfo#writeToParcel(android.os.Parcel, int) parameter #0: + Missing nullability on parameter `dest` in method `writeToParcel` MissingNullability: android.content.res.AssetManager#getOverlayablesToString(String) parameter #0: MissingNullability: android.content.res.Configuration#windowConfiguration: @@ -2433,6 +2459,38 @@ MutableBareField: android.content.AutofillOptions#disabledActivities: MutableBareField: android.content.AutofillOptions#whitelistedActivitiesForAugmentedAutofill: +MutableBareField: android.content.pm.UserInfo#convertedFromPreCreated: + Bare field convertedFromPreCreated must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#creationTime: + Bare field creationTime must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#flags: + Bare field flags must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#guestToRemove: + Bare field guestToRemove must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#iconPath: + Bare field iconPath must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#id: + Bare field id must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#lastLoggedInFingerprint: + Bare field lastLoggedInFingerprint must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#lastLoggedInTime: + Bare field lastLoggedInTime must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#name: + Bare field name must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#partial: + Bare field partial must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#preCreated: + Bare field preCreated must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#profileBadge: + Bare field profileBadge must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#profileGroupId: + Bare field profileGroupId must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#restrictedProfileParentId: + Bare field restrictedProfileParentId must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#serialNumber: + Bare field serialNumber must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#userType: + Bare field userType must be marked final, or moved behind accessors if mutable MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#cache: MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#dbName: @@ -2548,15 +2606,15 @@ NoSettingsProvider: android.provider.Settings.Global#TETHER_OFFLOAD_DISABLED: NoSettingsProvider: android.provider.Settings.Global#USE_OPEN_WIFI_PACKAGE: NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED: - + NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_CAPABILITY: - + NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE: - + NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_ALL: - + NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN: - + NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW: NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE: @@ -2619,6 +2677,10 @@ NotCloseable: android.telephony.ims.stub.ImsUtImplBase: +NullableCollection: android.os.UserManager#createProfileForUser(String, String, int, int, String[]) parameter #4: + Type of parameter disallowedPackages in android.os.UserManager.createProfileForUser(String name, String userType, int flags, int userId, String[] disallowedPackages) is a nullable collection (`java.lang.String[]`); must be non-null + + OnNameExpected: android.service.autofill.augmented.AugmentedAutofillService#dump(java.io.PrintWriter, String[]): OnNameExpected: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]): @@ -2729,6 +2791,8 @@ ParcelCreator: android.service.autofill.InternalValidator: ParcelNotFinal: android.app.WindowConfiguration: +ParcelNotFinal: android.content.pm.UserInfo: + Parcelable classes must be final: android.content.pm.UserInfo is not final ParcelNotFinal: android.net.metrics.IpConnectivityLog.Event: ParcelNotFinal: android.os.IncidentManager.IncidentReport: 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 220c332647a9..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. */ @@ -617,6 +619,7 @@ public class ActivityManager { 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}*/ @@ -3432,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 @@ -4776,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/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index c31c22cca329..71f164f49262 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -30,6 +30,7 @@ import android.content.pm.UserInfo; import android.net.Uri; import android.os.Bundle; import android.os.IBinder; +import android.os.PowerWhitelistManager.ReasonCode; import android.os.PowerWhitelistManager.TempAllowListType; import android.os.TransactionTooLargeException; import android.os.WorkSource; @@ -102,12 +103,19 @@ public abstract class ActivityManagerInternal { * Sets how long a {@link PendingIntent} can be temporarily allowlisted to bypass restrictions * such as Power Save mode. * @param target - * @param whitelistToken + * @param allowlistToken * @param duration temp allowlist duration in milliseconds. * @param type temp allowlist type defined at {@link TempAllowListType} + * @param reasonCode one of {@link ReasonCode} + * @param reason A human-readable reason for logging purposes. */ + public abstract void setPendingIntentAllowlistDuration(IIntentSender target, + IBinder allowlistToken, long duration, @TempAllowListType int type, + @ReasonCode int reasonCode, @Nullable String reason); + + @Deprecated public abstract void setPendingIntentWhitelistDuration(IIntentSender target, - IBinder whitelistToken, long duration, int type); + IBinder allowlistToken, long duration, @TempAllowListType int type); /** * Returns the flags set for a {@link PendingIntent}. @@ -127,20 +135,26 @@ public abstract class ActivityManagerInternal { IBinder allowlistToken); /** - * Allow DeviceIdleController to tell us about what apps are whitelisted. + * Allow DeviceIdleController to tell us about what apps are allowlisted. */ - public abstract void setDeviceIdleWhitelist(int[] allAppids, int[] exceptIdleAppids); + public abstract void setDeviceIdleAllowlist(int[] allAppids, int[] exceptIdleAppids); /** - * Update information about which app IDs are on the temp whitelist. + * Update information about which app IDs are on the temp allowlist. * @param appids the updated list of appIds in temp allowlist. * @param changingUid uid to add or remove to temp allowlist. * @param adding true to add to temp allowlist, false to remove from temp allowlist. * @param durationMs when adding is true, the duration to be in temp allowlist. * @param type temp allowlist type defined at {@link TempAllowListType}. + * @param reasonCode one of {@link ReasonCode} + * @param reason A human-readable reason for logging purposes. + * @param callingUid the callingUid that setup this temp allowlist, only valid when param adding + * is true. */ - public abstract void updateDeviceIdleTempWhitelist(int[] appids, int changingUid, - boolean adding, long durationMs, @TempAllowListType int type); + public abstract void updateDeviceIdleTempAllowlist(int[] appids, int changingUid, + boolean adding, long durationMs, @TempAllowListType int type, + @ReasonCode int reasonCode, + @Nullable String reason, int callingUid); /** * Get the procstate for the UID. The return value will be between @@ -335,10 +349,11 @@ public abstract class ActivityManagerInternal { * @param targetUid the UID that is been temp allowlisted. * @param duration temp allowlist duration in milliseconds. * @param type temp allowlist type defined at {@link TempAllowListType} - * @param tag + * @param reasonCode one of {@link ReasonCode} + * @param reason */ - public abstract void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid, - long duration, int type, String tag); + public abstract void tempAllowlistForPendingIntent(int callerPid, int callerUid, int targetUid, + long duration, int type, @ReasonCode int reasonCode, String reason); public abstract int broadcastIntentInPackage(String packageName, @Nullable String featureId, int uid, int realCallingUid, int realCallingPid, Intent intent, String resolvedType, @@ -495,9 +510,9 @@ public abstract class ActivityManagerInternal { /** * Sends a broadcast, assuming the caller to be the system and allowing the inclusion of an - * approved whitelist of app Ids >= {@link android.os.Process#FIRST_APPLICATION_UID} that the + * approved allowlist of app Ids >= {@link android.os.Process#FIRST_APPLICATION_UID} that the * broadcast my be sent to; any app Ids < {@link android.os.Process#FIRST_APPLICATION_UID} are - * automatically whitelisted. + * automatically allowlisted. * * @see com.android.server.am.ActivityManagerService#broadcastIntentWithFeature( * IApplicationThread, String, Intent, String, IIntentReceiver, int, String, Bundle, diff --git a/core/java/android/app/AnrController.java b/core/java/android/app/AnrController.java index cfc9d2715720..a0d4b3a6a753 100644 --- a/core/java/android/app/AnrController.java +++ b/core/java/android/app/AnrController.java @@ -23,7 +23,29 @@ package android.app; public interface AnrController { /** * Returns the delay in milliseconds for an ANR dialog that is about to be shown for - * {@code packageName}. + * {@code packageName} with {@code uid}. + * + * Implementations should only return a positive value if they actually expect the + * {@code packageName} to be delayed due to them. + + * If there are multiple controllers registered, the controller with the max delay will + * be selected and will receive an {@link #onAnrDelayStarted} callback at the start of the + * delay and an {@link #onAnrDelayCompleted} at the end of the delay. */ long getAnrDelayMillis(String packageName, int uid); + + /** + * Notifies the controller at the start of the ANR dialog delay for {@code packageName} with + * {@code uid}. The controller can decide to show a progress UI after this notification. + */ + void onAnrDelayStarted(String packageName, int uid); + + /** + * Notifies the controller at the end of the ANR dialog delay for {@code packageName} with + * {@code uid}. + * + * @return whether the ANR dialog should be shown or cancelled. {@code true} if the + * ANR dialog should be shown, {@code false} if it should be cancelled. + */ + boolean onAnrDelayCompleted(String packageName, int uid); } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 2f3b50b17d51..160844aacc46 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1141,23 +1141,20 @@ public class AppOpsManager { * * @hide */ - // TODO: Add as AppProtoEnums - public static final int OP_PHONE_CALL_MICROPHONE = 100; + public static final int OP_PHONE_CALL_MICROPHONE = AppProtoEnums.APP_OP_PHONE_CALL_MICROPHONE; /** * Phone call is using camera * * @hide */ - // TODO: Add as AppProtoEnums - public static final int OP_PHONE_CALL_CAMERA = 101; + public static final int OP_PHONE_CALL_CAMERA = AppProtoEnums.APP_OP_PHONE_CALL_CAMERA; /** * Audio is being recorded for hotword detection. * * @hide */ - // TODO: Add as AppProtoEnums - public static final int OP_RECORD_AUDIO_HOTWORD = 102; + public static final int OP_RECORD_AUDIO_HOTWORD = AppProtoEnums.APP_OP_RECORD_AUDIO_HOTWORD; /** * Manage credentials in the system KeyChain. @@ -1184,10 +1181,29 @@ public class AppOpsManager { */ public static final int OP_SCHEDULE_EXACT_ALARM = AppProtoEnums.APP_OP_SCHEDULE_EXACT_ALARM; + /** + * Fine location being accessed by a location source, which is + * a component that already has location data since it is the one + * that produces location, which is it is a data source for + * location data. + * + * @hide + */ + public static final int OP_FINE_LOCATION_SOURCE = AppProtoEnums.APP_OP_FINE_LOCATION_SOURCE; + + /** + * Coarse location being accessed by a location source, which is + * a component that already has location data since it is the one + * that produces location, which is it is a data source for + * location data. + * + * @hide + */ + public static final int OP_COARSE_LOCATION_SOURCE = AppProtoEnums.APP_OP_COARSE_LOCATION_SOURCE; /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final int _NUM_OP = 108; + public static final int _NUM_OP = 110; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -1567,6 +1583,24 @@ public class AppOpsManager { */ public static final String OPSTR_SCHEDULE_EXACT_ALARM = "android:schedule_exact_alarm"; + /** + * Fine location being accessed by a location source, which is + * a component that already has location since it is the one that + * produces location. + * + * @hide + */ + public static final String OPSTR_FINE_LOCATION_SOURCE = "android:fine_location_source"; + + /** + * Coarse location being accessed by a location source, which is + * a component that already has location since it is the one that + * produces location. + * + * @hide + */ + public static final String OPSTR_COARSE_LOCATION_SOURCE = "android:coarse_location_source"; + /** {@link #sAppOpsToNote} not initialized yet for this op */ private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0; /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */ @@ -1767,6 +1801,8 @@ public class AppOpsManager { OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER OP_RECORD_AUDIO_OUTPUT, // RECORD_AUDIO_OUTPUT OP_SCHEDULE_EXACT_ALARM, // SCHEDULE_EXACT_ALARM + OP_FINE_LOCATION, // OP_FINE_LOCATION_SOURCE + OP_COARSE_LOCATION, // OP_COARSE_LOCATION_SOURCE }; /** @@ -1881,6 +1917,8 @@ public class AppOpsManager { OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, OPSTR_RECORD_AUDIO_OUTPUT, OPSTR_SCHEDULE_EXACT_ALARM, + OPSTR_FINE_LOCATION_SOURCE, + OPSTR_COARSE_LOCATION_SOURCE, }; /** @@ -1996,6 +2034,8 @@ public class AppOpsManager { "USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER", "RECORD_AUDIO_OUTPUT", "SCHEDULE_EXACT_ALARM", + "FINE_LOCATION_SOURCE", + "COARSE_LOCATION_SOURCE", }; /** @@ -2112,6 +2152,8 @@ public class AppOpsManager { Manifest.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, null, // no permission for OP_RECORD_AUDIO_OUTPUT Manifest.permission.SCHEDULE_EXACT_ALARM, + null, // no permission for OP_ACCESS_FINE_LOCATION_SOURCE, + null, // no permission for OP_ACCESS_COARSE_LOCATION_SOURCE, }; /** @@ -2228,6 +2270,8 @@ public class AppOpsManager { null, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER null, // RECORD_AUDIO_OUTPUT null, // SCHEDULE_EXACT_ALARM + null, // ACCESS_FINE_LOCATION_SOURCE + null, // ACCESS_COARSE_LOCATION_SOURCE }; /** @@ -2343,6 +2387,8 @@ public class AppOpsManager { null, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER null, // RECORD_AUDIO_OUTPUT null, // SCHEDULE_EXACT_ALARM + null, // ACCESS_FINE_LOCATION_SOURCE + null, // ACCESS_COARSE_LOCATION_SOURCE }; /** @@ -2457,6 +2503,8 @@ public class AppOpsManager { AppOpsManager.MODE_DEFAULT, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER AppOpsManager.MODE_ALLOWED, // RECORD_AUDIO_OUTPUT AppOpsManager.MODE_DEFAULT, // SCHEDULE_EXACT_ALARM + AppOpsManager.MODE_ALLOWED, // ACCESS_FINE_LOCATION_SOURCE + AppOpsManager.MODE_ALLOWED, // ACCESS_COARSE_LOCATION_SOURCE }; /** @@ -2575,6 +2623,8 @@ public class AppOpsManager { true, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER false, // RECORD_AUDIO_OUTPUT false, // SCHEDULE_EXACT_ALARM + false, // ACCESS_FINE_LOCATION_SOURCE + false, // ACCESS_COARSE_LOCATION_SOURCE }; /** diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 062cab457ebe..a6260d6d9cad 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1797,6 +1797,7 @@ public class ApplicationPackageManager extends PackageManager { } } + @UnsupportedAppUsage protected ApplicationPackageManager(ContextImpl context, IPackageManager pm) { mContext = context; mPM = pm; diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index 445fdd83f34a..2e06e9b80595 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -16,11 +16,13 @@ package android.app; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.os.Build; import android.os.Bundle; import android.os.PowerWhitelistManager; +import android.os.PowerWhitelistManager.ReasonCode; import android.os.PowerWhitelistManager.TempAllowListType; /** @@ -31,8 +33,11 @@ import android.os.PowerWhitelistManager.TempAllowListType; */ @SystemApi public class BroadcastOptions { - private long mTemporaryAppWhitelistDuration; - private @TempAllowListType int mTemporaryAppWhitelistType; + private long mTemporaryAppAllowlistDuration; + private @TempAllowListType int mTemporaryAppAllowlistType; + private @ReasonCode int mTemporaryAppAllowlistReasonCode = + PowerWhitelistManager.REASON_UNKNOWN; + private @Nullable String mTemporaryAppAllowlistReason; private int mMinManifestReceiverApiLevel = 0; private int mMaxManifestReceiverApiLevel = Build.VERSION_CODES.CUR_DEVELOPMENT; private boolean mDontSendToRestrictedApps = false; @@ -42,11 +47,17 @@ public class BroadcastOptions { * How long to temporarily put an app on the power allowlist when executing this broadcast * to it. */ - static final String KEY_TEMPORARY_APP_WHITELIST_DURATION - = "android:broadcast.temporaryAppWhitelistDuration"; + static final String KEY_TEMPORARY_APP_ALLOWLIST_DURATION + = "android:broadcast.temporaryAppAllowlistDuration"; - static final String KEY_TEMPORARY_APP_WHITELIST_TYPE - = "android:broadcast.temporaryAppWhitelistType"; + static final String KEY_TEMPORARY_APP_ALLOWLIST_TYPE + = "android:broadcast.temporaryAppAllowlistType"; + + static final String KEY_TEMPORARY_APP_ALLOWLIST_REASON_CODE = + "android:broadcast.temporaryAppAllowlistReasonCode"; + + static final String KEY_TEMPORARY_APP_ALLOWLIST_REASON = + "android:broadcast.temporaryAppAllowlistReason"; /** * Corresponds to {@link #setMinManifestReceiverApiLevel}. @@ -80,6 +91,7 @@ public class BroadcastOptions { @Deprecated public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED = PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED; + /** * @hide * @deprecated Use {@link android.os.PowerWhitelistManager# @@ -99,8 +111,11 @@ public class BroadcastOptions { /** @hide */ public BroadcastOptions(Bundle opts) { - mTemporaryAppWhitelistDuration = opts.getLong(KEY_TEMPORARY_APP_WHITELIST_DURATION); - mTemporaryAppWhitelistType = opts.getInt(KEY_TEMPORARY_APP_WHITELIST_TYPE); + mTemporaryAppAllowlistDuration = opts.getLong(KEY_TEMPORARY_APP_ALLOWLIST_DURATION); + mTemporaryAppAllowlistType = opts.getInt(KEY_TEMPORARY_APP_ALLOWLIST_TYPE); + mTemporaryAppAllowlistReasonCode = opts.getInt(KEY_TEMPORARY_APP_ALLOWLIST_REASON_CODE, + PowerWhitelistManager.REASON_UNKNOWN); + mTemporaryAppAllowlistReason = opts.getString(KEY_TEMPORARY_APP_ALLOWLIST_REASON); mMinManifestReceiverApiLevel = opts.getInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, 0); mMaxManifestReceiverApiLevel = opts.getInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL, Build.VERSION_CODES.CUR_DEVELOPMENT); @@ -113,14 +128,16 @@ public class BroadcastOptions { * Set a duration for which the system should temporary place an application on the * power allowlist when this broadcast is being delivered to it. * @param duration The duration in milliseconds; 0 means to not place on allowlist. + * @deprecated use {@link #setTemporaryAppAllowlist(long, int, int, String)} instead. */ + @Deprecated @RequiresPermission(anyOf = {android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(long duration) { - mTemporaryAppWhitelistDuration = duration; - mTemporaryAppWhitelistType = - PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED; + setTemporaryAppAllowlist(duration, + PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, + PowerWhitelistManager.REASON_UNKNOWN, null); } /** @@ -129,29 +146,69 @@ public class BroadcastOptions { * type. * @param type one of {@link TempAllowListType} * @param duration the duration in milliseconds; 0 means to not place on allowlist. + * @deprecated use {@link #setTemporaryAppAllowlist(long, int, int, String)} instead. */ + @Deprecated @RequiresPermission(anyOf = {android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(@TempAllowListType int type, long duration) { - mTemporaryAppWhitelistDuration = duration; - mTemporaryAppWhitelistType = type; + setTemporaryAppAllowlist(duration, type, + PowerWhitelistManager.REASON_UNKNOWN, null); } /** - * Return {@link #setTemporaryAppWhitelistDuration}. + * Set a duration for which the system should temporary place an application on the + * power allowlist when this broadcast is being delivered to it, specify the temp allowlist + * type. + * @param duration the duration in milliseconds; 0 means to not place on allowlist. + * @param type one of {@link TempAllowListType} + * @param reasonCode one of {@link ReasonCode}, use + * {@link PowerWhitelistManager#REASON_UNKNOWN} if not sure. + * @param reason A human-readable reason explaining why the app is temp allowlisted. Only + * used for logging purposes. Could be null or empty string. + */ + @RequiresPermission(anyOf = {android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, + android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, + android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) + public void setTemporaryAppAllowlist(long duration, @TempAllowListType int type, + @ReasonCode int reasonCode, @Nullable String reason) { + mTemporaryAppAllowlistDuration = duration; + mTemporaryAppAllowlistType = type; + mTemporaryAppAllowlistReasonCode = reasonCode; + mTemporaryAppAllowlistReason = reason; + } + + /** + * Return {@link #setTemporaryAppAllowlist}. + * @hide + */ + public long getTemporaryAppAllowlistDuration() { + return mTemporaryAppAllowlistDuration; + } + + /** + * Return {@link #mTemporaryAppAllowlistType}. * @hide */ - public long getTemporaryAppWhitelistDuration() { - return mTemporaryAppWhitelistDuration; + public @TempAllowListType int getTemporaryAppAllowlistType() { + return mTemporaryAppAllowlistType; } /** - * Return {@link #mTemporaryAppWhitelistType}. + * Return {@link #mTemporaryAppAllowlistReasonCode}. * @hide */ - public @TempAllowListType int getTemporaryAppWhitelistType() { - return mTemporaryAppWhitelistType; + public @ReasonCode int getTemporaryAppAllowlistReasonCode() { + return mTemporaryAppAllowlistReasonCode; + } + + /** + * Return {@link #mTemporaryAppAllowlistReason}. + * @hide + */ + public @Nullable String getTemporaryAppAllowlistReason() { + return mTemporaryAppAllowlistReason; } /** @@ -236,11 +293,17 @@ public class BroadcastOptions { */ public Bundle toBundle() { Bundle b = new Bundle(); - if (mTemporaryAppWhitelistDuration > 0) { - b.putLong(KEY_TEMPORARY_APP_WHITELIST_DURATION, mTemporaryAppWhitelistDuration); + if (mTemporaryAppAllowlistDuration > 0) { + b.putLong(KEY_TEMPORARY_APP_ALLOWLIST_DURATION, mTemporaryAppAllowlistDuration); + } + if (mTemporaryAppAllowlistType != 0) { + b.putInt(KEY_TEMPORARY_APP_ALLOWLIST_TYPE, mTemporaryAppAllowlistType); + } + if (mTemporaryAppAllowlistReasonCode != PowerWhitelistManager.REASON_UNKNOWN) { + b.putInt(KEY_TEMPORARY_APP_ALLOWLIST_REASON_CODE, mTemporaryAppAllowlistReasonCode); } - if (mTemporaryAppWhitelistType != 0) { - b.putInt(KEY_TEMPORARY_APP_WHITELIST_TYPE, mTemporaryAppWhitelistType); + if (mTemporaryAppAllowlistReason != null) { + b.putString(KEY_TEMPORARY_APP_ALLOWLIST_REASON, mTemporaryAppAllowlistReason); } if (mMinManifestReceiverApiLevel != 0) { b.putInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, mMinManifestReceiverApiLevel); diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 4ad13e1932dd..3a8172ea98b8 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, @@ -552,7 +549,7 @@ interface IActivityManager { /** * Add a bare uid to the background restrictions whitelist. Only the system uid may call this. */ - void backgroundWhitelistUid(int uid); + void backgroundAllowlistUid(int uid); // Start of P transactions /** @@ -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/PendingIntent.java b/core/java/android/app/PendingIntent.java index 671315f37b20..549bd4b9fe6a 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; @@ -55,6 +61,7 @@ import com.android.internal.os.IResultReceiver; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; +import java.util.Objects; /** * A description of an Intent and target action to perform with it. Instances @@ -122,6 +129,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. @@ -455,15 +465,14 @@ public final class PendingIntent implements Parcelable { public static PendingIntent getActivityAsUser(Context context, int requestCode, @NonNull Intent intent, int flags, Bundle options, UserHandle user) { String packageName = context.getPackageName(); - String resolvedType = intent != null ? intent.resolveTypeIfNeeded( - context.getContentResolver()) : null; + String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver()); checkFlags(flags, packageName); try { intent.migrateExtraStreamToClipData(context); 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; @@ -630,7 +639,7 @@ public final class PendingIntent implements Parcelable { */ @SuppressWarnings("AndroidFrameworkPendingIntentMutability") public static PendingIntent getBroadcast(Context context, int requestCode, - Intent intent, @Flags int flags) { + @NonNull Intent intent, @Flags int flags) { return getBroadcastAsUser(context, requestCode, intent, flags, context.getUser()); } @@ -643,14 +652,13 @@ public final class PendingIntent implements Parcelable { public static PendingIntent getBroadcastAsUser(Context context, int requestCode, Intent intent, int flags, UserHandle userHandle) { String packageName = context.getPackageName(); - String resolvedType = intent != null ? intent.resolveTypeIfNeeded( - context.getContentResolver()) : null; + String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver()); checkFlags(flags, packageName); try { 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 +695,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,14 +725,13 @@ 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, Intent intent, int flags, int serviceKind) { String packageName = context.getPackageName(); - String resolvedType = intent != null ? intent.resolveTypeIfNeeded( - context.getContentResolver()) : null; + String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver()); checkFlags(flags, packageName); try { intent.prepareToLeaveProcess(context); @@ -746,6 +753,7 @@ public final class PendingIntent implements Parcelable { * @return Returns a IntentSender object that wraps the sender of PendingIntent * */ + @NonNull public IntentSender getIntentSender() { return new IntentSender(mTarget, mWhitelistToken); } @@ -758,6 +766,7 @@ public final class PendingIntent implements Parcelable { try { ActivityManager.getService().cancelIntentSender(mTarget); } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } } @@ -1000,13 +1009,9 @@ public final class PendingIntent implements Parcelable { * @deprecated Renamed to {@link #getCreatorPackage()}. */ @Deprecated + @NonNull public String getTargetPackage() { - try { - return ActivityManager.getService() - .getPackageForIntentSender(mTarget); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return getCreatorPackage(); } /** @@ -1024,17 +1029,11 @@ public final class PendingIntent implements Parcelable { * only use this information to identify who you expect to be interacting with * through a {@link #send} call, not who gave you the PendingIntent.</p> * - * @return The package name of the PendingIntent, or null if there is - * none associated with it. + * @return The package name of the PendingIntent. */ - @Nullable + @NonNull 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(); } /** @@ -1149,18 +1143,12 @@ public final class PendingIntent implements Parcelable { * only use this information to identify who you expect to be interacting with * through a {@link #send} call, not who gave you the PendingIntent.</p> * - * @return The user handle of the PendingIntent, or null if there is - * none associated with it. + * @return The user handle of the PendingIntent */ - @Nullable + @NonNull 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 UserHandle.getUserHandleForUid(uid); } /** @@ -1180,12 +1168,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 +1176,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; } /** @@ -1318,7 +1281,7 @@ public final class PendingIntent implements Parcelable { sb.append("PendingIntent{"); sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(": "); - sb.append(mTarget != null ? mTarget.asBinder() : null); + sb.append(mTarget.asBinder()); sb.append('}'); return sb.toString(); } @@ -1326,9 +1289,7 @@ public final class PendingIntent implements Parcelable { /** @hide */ public void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); - if (mTarget != null) { - proto.write(PendingIntentProto.TARGET, mTarget.asBinder().toString()); - } + proto.write(PendingIntentProto.TARGET, mTarget.asBinder().toString()); proto.end(token); } @@ -1345,8 +1306,7 @@ public final class PendingIntent implements Parcelable { } - public static final @android.annotation.NonNull Parcelable.Creator<PendingIntent> CREATOR - = new Parcelable.Creator<PendingIntent>() { + public static final @NonNull Creator<PendingIntent> CREATOR = new Creator<PendingIntent>() { public PendingIntent createFromParcel(Parcel in) { IBinder target = in.readStrongBinder(); return target != null @@ -1400,11 +1360,11 @@ public final class PendingIntent implements Parcelable { * @hide */ public PendingIntent(IIntentSender target) { - mTarget = target; + mTarget = Objects.requireNonNull(target); } /*package*/ PendingIntent(IBinder target, Object cookie) { - mTarget = IIntentSender.Stub.asInterface(target); + mTarget = Objects.requireNonNull(IIntentSender.Stub.asInterface(target)); if (cookie != null) { mWhitelistToken = (IBinder)cookie; } @@ -1433,4 +1393,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/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/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java index 4ae1670e9041..d04ca1d9a48e 100644 --- a/core/java/android/app/WindowConfiguration.java +++ b/core/java/android/app/WindowConfiguration.java @@ -57,7 +57,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu * TODO: Investigate combining with {@link #mAppBounds}. Can the latter be a product of the * former? */ - private Rect mBounds = new Rect(); + private final Rect mBounds = new Rect(); /** * {@link android.graphics.Rect} defining app bounds. The dimensions override usages of @@ -71,7 +71,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu * The maximum {@link Rect} bounds that an app can expect. It is used to report value of * {@link WindowManager#getMaximumWindowMetrics()}. */ - private Rect mMaxBounds = new Rect(); + private final Rect mMaxBounds = new Rect(); /** * The current rotation of this window container relative to the default @@ -240,9 +240,9 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeParcelable(mBounds, flags); - dest.writeParcelable(mAppBounds, flags); - dest.writeParcelable(mMaxBounds, flags); + mBounds.writeToParcel(dest, flags); + dest.writeTypedObject(mAppBounds, flags); + mMaxBounds.writeToParcel(dest, flags); dest.writeInt(mWindowingMode); dest.writeInt(mActivityType); dest.writeInt(mAlwaysOnTop); @@ -250,10 +250,11 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu dest.writeInt(mDisplayWindowingMode); } - private void readFromParcel(Parcel source) { - mBounds = source.readParcelable(Rect.class.getClassLoader()); - mAppBounds = source.readParcelable(Rect.class.getClassLoader()); - mMaxBounds = source.readParcelable(Rect.class.getClassLoader()); + /** @hide */ + public void readFromParcel(@NonNull Parcel source) { + mBounds.readFromParcel(source); + mAppBounds = source.readTypedObject(Rect.CREATOR); + mMaxBounds.readFromParcel(source); mWindowingMode = source.readInt(); mActivityType = source.readInt(); mAlwaysOnTop = source.readInt(); @@ -693,9 +694,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu } protoOutputStream.write(WINDOWING_MODE, mWindowingMode); protoOutputStream.write(ACTIVITY_TYPE, mActivityType); - if (mBounds != null) { - mBounds.dumpDebug(protoOutputStream, BOUNDS); - } + mBounds.dumpDebug(protoOutputStream, BOUNDS); mMaxBounds.dumpDebug(protoOutputStream, MAX_BOUNDS); protoOutputStream.end(token); } @@ -719,11 +718,9 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu mAppBounds.readFromProto(proto, APP_BOUNDS); break; case (int) BOUNDS: - mBounds = new Rect(); mBounds.readFromProto(proto, BOUNDS); break; case (int) MAX_BOUNDS: - mMaxBounds = new Rect(); mMaxBounds.readFromProto(proto, MAX_BOUNDS); break; case (int) WINDOWING_MODE: 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 132af4b2f67b..dd2ba7db03ae 100644 --- a/core/java/android/app/people/PeopleSpaceTile.java +++ b/core/java/android/app/people/PeopleSpaceTile.java @@ -29,6 +29,7 @@ import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; import java.util.ArrayList; import java.util.List; @@ -45,7 +46,7 @@ public class PeopleSpaceTile implements Parcelable { private String mId; private CharSequence mUserName; private Icon mUserIcon; - private int mUid; + private UserHandle mUserHandle; private Uri mContactUri; private String mPackageName; private String mBirthdayText; @@ -64,7 +65,7 @@ public class PeopleSpaceTile implements Parcelable { mUserName = b.mUserName; mUserIcon = b.mUserIcon; mContactUri = b.mContactUri; - mUid = b.mUid; + mUserHandle = b.mUserHandle; mPackageName = b.mPackageName; mBirthdayText = b.mBirthdayText; mLastInteractionTimestamp = b.mLastInteractionTimestamp; @@ -95,8 +96,8 @@ public class PeopleSpaceTile implements Parcelable { return mContactUri; } - public int getUid() { - return mUid; + public UserHandle getUserHandle() { + return mUserHandle; } public String getPackageName() { @@ -165,7 +166,7 @@ public class PeopleSpaceTile implements Parcelable { Builder builder = new Builder(mId, mUserName.toString(), mUserIcon, mIntent); builder.setContactUri(mContactUri); - builder.setUid(mUid); + builder.setUserHandle(mUserHandle); builder.setPackageName(mPackageName); builder.setBirthdayText(mBirthdayText); builder.setLastInteractionTimestamp(mLastInteractionTimestamp); @@ -186,7 +187,7 @@ public class PeopleSpaceTile implements Parcelable { private CharSequence mUserName; private Icon mUserIcon; private Uri mContactUri; - private int mUid; + private UserHandle mUserHandle; private String mPackageName; private String mBirthdayText; private long mLastInteractionTimestamp; @@ -212,7 +213,7 @@ public class PeopleSpaceTile implements Parcelable { mId = info.getId(); mUserName = info.getLabel(); mUserIcon = convertDrawableToIcon(launcherApps.getShortcutIconDrawable(info, 0)); - mUid = info.getUserId(); + mUserHandle = info.getUserHandle(); mPackageName = info.getPackage(); mContactUri = getContactUri(info); } @@ -222,7 +223,7 @@ public class PeopleSpaceTile implements Parcelable { mId = info.getId(); mUserName = info.getLabel(); mUserIcon = convertDrawableToIcon(launcherApps.getShortcutIconDrawable(info, 0)); - mUid = info.getUserId(); + mUserHandle = info.getUserHandle(); mPackageName = info.getPackage(); mContactUri = getContactUri(info); mStatuses = channel.getStatuses(); @@ -265,9 +266,9 @@ public class PeopleSpaceTile implements Parcelable { return this; } - /** Sets the associated uid. */ - public Builder setUid(int uid) { - mUid = uid; + /** Sets the associated {@code userHandle}. */ + public Builder setUserHandle(UserHandle userHandle) { + mUserHandle = userHandle; return this; } @@ -349,7 +350,7 @@ public class PeopleSpaceTile implements Parcelable { mUserName = in.readCharSequence(); mUserIcon = in.readParcelable(Icon.class.getClassLoader()); mContactUri = in.readParcelable(Uri.class.getClassLoader()); - mUid = in.readInt(); + mUserHandle = in.readParcelable(UserHandle.class.getClassLoader()); mPackageName = in.readString(); mBirthdayText = in.readString(); mLastInteractionTimestamp = in.readLong(); @@ -375,7 +376,7 @@ public class PeopleSpaceTile implements Parcelable { dest.writeCharSequence(mUserName); dest.writeParcelable(mUserIcon, flags); dest.writeParcelable(mContactUri, flags); - dest.writeInt(mUid); + dest.writeParcelable(mUserHandle, flags); dest.writeString(mPackageName); dest.writeString(mBirthdayText); dest.writeLong(mLastInteractionTimestamp); diff --git a/core/java/android/app/time/LocationTimeZoneManager.java b/core/java/android/app/time/LocationTimeZoneManager.java index 71a800f2085e..066aadae1476 100644 --- a/core/java/android/app/time/LocationTimeZoneManager.java +++ b/core/java/android/app/time/LocationTimeZoneManager.java @@ -37,7 +37,7 @@ public final class LocationTimeZoneManager { /** * The name of the service for shell commands */ - public static final String SHELL_COMMAND_SERVICE_NAME = "location_time_zone_manager"; + public static final String SERVICE_NAME = "location_time_zone_manager"; /** * A shell command that starts the service (after stop). diff --git a/core/java/android/companion/DeviceNotAssociatedException.java b/core/java/android/companion/DeviceNotAssociatedException.java index bebb6c9ff7eb..f8a7a7cdeffe 100644 --- a/core/java/android/companion/DeviceNotAssociatedException.java +++ b/core/java/android/companion/DeviceNotAssociatedException.java @@ -23,7 +23,7 @@ import android.annotation.Nullable; * An exception for a case when a given device was not * {@link CompanionDeviceManager#associate associated} to the calling app. */ -public class DeviceNotAssociatedException extends Exception { +public class DeviceNotAssociatedException extends RuntimeException { /** @hide */ public DeviceNotAssociatedException(@Nullable String deviceName) { super("Device not associated with the current app: " + deviceName); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index e20f706c2c35..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 @@ -6674,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 @@ -6694,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/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/DataLoaderParamsParcel.aidl b/core/java/android/content/pm/DataLoaderParamsParcel.aidl index d40012fd5718..29d472e6e927 100644 --- a/core/java/android/content/pm/DataLoaderParamsParcel.aidl +++ b/core/java/android/content/pm/DataLoaderParamsParcel.aidl @@ -23,7 +23,7 @@ import android.content.pm.DataLoaderType; * @hide */ parcelable DataLoaderParamsParcel { - DataLoaderType type; + DataLoaderType type = DataLoaderType.NONE; @utf8InCpp String packageName; @utf8InCpp String className; @utf8InCpp String arguments; diff --git a/core/java/android/content/pm/InstallationFileParcel.aidl b/core/java/android/content/pm/InstallationFileParcel.aidl index b7efc1947cc3..09d1a3291b69 100644 --- a/core/java/android/content/pm/InstallationFileParcel.aidl +++ b/core/java/android/content/pm/InstallationFileParcel.aidl @@ -24,7 +24,7 @@ import android.content.pm.InstallationFileLocation; */ parcelable InstallationFileParcel { String name; - InstallationFileLocation location; + InstallationFileLocation location = InstallationFileLocation.UNKNOWN; long size; byte[] metadata; byte[] signature; diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 567501cba70b..42cbe3586db3 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1173,7 +1173,6 @@ public class PackageInstaller { * {@hide} */ @SystemApi - @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2) public @Nullable DataLoaderParams getDataLoaderParams() { try { DataLoaderParamsParcel data = mSession.getDataLoaderParams(); @@ -1213,7 +1212,6 @@ public class PackageInstaller { * {@hide} */ @SystemApi - @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2) public void addFile(@FileLocation int location, @NonNull String name, long lengthBytes, @NonNull byte[] metadata, @Nullable byte[] signature) { try { @@ -1237,7 +1235,6 @@ public class PackageInstaller { * {@hide} */ @SystemApi - @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2) public void removeFile(@FileLocation int location, @NonNull String name) { try { mSession.removeFile(location, name); @@ -2050,9 +2047,7 @@ public class PackageInstaller { * {@hide} */ @SystemApi - @RequiresPermission(allOf = { - Manifest.permission.INSTALL_PACKAGES, - Manifest.permission.USE_INSTALLER_V2}) + @RequiresPermission(Manifest.permission.INSTALL_PACKAGES) public void setDataLoaderParams(@NonNull DataLoaderParams dataLoaderParams) { this.dataLoaderParams = dataLoaderParams; } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index f9122b1f6c8b..ca882417394e 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 @@ -3623,11 +3623,26 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device supports a enabling/disabling sensor privacy for - * camera. When sensory privacy for the camera is enabled no camera data is send to clients, + * microphone. When sensory privacy for the microphone is enabled no microphone data is sent to + * clients, e.g. all audio data is silent. + * + * @hide + */ + @SystemApi + @TestApi + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device supports a enabling/disabling sensor privacy for + * camera. When sensory privacy for the camera is enabled no camera data is sent to clients, * e.g. the view finder in a camera app would appear blank. * * @hide */ + @SystemApi + @TestApi @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle"; diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java index d81dff8f2908..cfb6e1b572aa 100644 --- a/core/java/android/content/pm/UserInfo.java +++ b/core/java/android/content/pm/UserInfo.java @@ -18,6 +18,7 @@ package android.content.pm; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.TestApi; import android.annotation.UserIdInt; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; @@ -47,6 +48,7 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ +@TestApi public class UserInfo implements Parcelable { /** 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/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 2f7aeb80986b..b66f048b829d 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -1956,7 +1956,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration dest.writeInt(mnc); fixUpLocaleList(); - dest.writeParcelable(mLocaleList, flags); + dest.writeTypedObject(mLocaleList, flags); if(userSetLocale) { dest.writeInt(1); @@ -1980,7 +1980,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration dest.writeInt(compatScreenWidthDp); dest.writeInt(compatScreenHeightDp); dest.writeInt(compatSmallestScreenWidthDp); - dest.writeValue(windowConfiguration); + windowConfiguration.writeToParcel(dest, flags); dest.writeInt(assetsSeq); dest.writeInt(seq); dest.writeInt(fontWeightAdjustment); @@ -1991,7 +1991,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration mcc = source.readInt(); mnc = source.readInt(); - mLocaleList = source.readParcelable(LocaleList.class.getClassLoader()); + mLocaleList = source.readTypedObject(LocaleList.CREATOR); locale = mLocaleList.get(0); userSetLocale = (source.readInt()==1); @@ -2012,7 +2012,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration compatScreenWidthDp = source.readInt(); compatScreenHeightDp = source.readInt(); compatSmallestScreenWidthDp = source.readInt(); - windowConfiguration.setTo((WindowConfiguration) source.readValue(null)); + windowConfiguration.readFromParcel(source); assetsSeq = source.readInt(); seq = source.readInt(); fontWeightAdjustment = source.readInt(); diff --git a/core/java/android/graphics/fonts/FontManager.java b/core/java/android/graphics/fonts/FontManager.java index e512cf1bbb1f..429eef95f952 100644 --- a/core/java/android/graphics/fonts/FontManager.java +++ b/core/java/android/graphics/fonts/FontManager.java @@ -20,7 +20,6 @@ import android.Manifest; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; @@ -29,7 +28,6 @@ import android.content.Context; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.text.FontConfig; -import android.util.Log; import com.android.internal.graphics.fonts.IFontManager; @@ -198,12 +196,11 @@ public class FontManager { * @return The current font configuration. null if failed to fetch information from the system * service. */ - public @Nullable FontConfig getFontConfig() { + public @NonNull FontConfig getFontConfig() { try { return mIFontManager.getFontConfig(); } catch (RemoteException e) { - Log.e(TAG, "Failed to call getFontConfig", e); - return null; + throw e.rethrowAsRuntimeException(); } } diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java index f4f9e1775d1a..e03c1f48773a 100644 --- a/core/java/android/hardware/SensorPrivacyManager.java +++ b/core/java/android/hardware/SensorPrivacyManager.java @@ -19,6 +19,7 @@ package android.hardware; 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; @@ -33,6 +34,7 @@ import com.android.internal.annotations.GuardedBy; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; /** * This class provides access to the sensor privacy services; sensor privacy allows the @@ -42,9 +44,21 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ +@SystemApi @TestApi @SystemService(Context.SENSOR_PRIVACY_SERVICE) public final class SensorPrivacyManager { + + /** + * @hide + */ + public static final boolean USE_MICROPHONE_TOGGLE = true; + + /** + * @hide + */ + public static final boolean USE_CAMERA_TOGGLE = true; + /** * Unique Id of this manager to identify to the service * @hide @@ -58,28 +72,39 @@ public final class SensorPrivacyManager { public static final String EXTRA_SENSOR = SensorPrivacyManager.class.getName() + ".extra.sensor"; - /** Microphone - * @hide */ - @TestApi - public static final int INDIVIDUAL_SENSOR_MICROPHONE = - SensorPrivacyIndividualEnabledSensorProto.MICROPHONE; - - /** Camera - * @hide */ - @TestApi - public static final int INDIVIDUAL_SENSOR_CAMERA = - SensorPrivacyIndividualEnabledSensorProto.CAMERA; - /** - * Individual sensors not listed in {@link Sensor} + * Individual sensors not listed in {@link Sensors} * @hide */ - @IntDef(prefix = "INDIVIDUAL_SENSOR_", value = { - INDIVIDUAL_SENSOR_MICROPHONE, - INDIVIDUAL_SENSOR_CAMERA - }) - @Retention(RetentionPolicy.SOURCE) - public @interface IndividualSensor {} + @SystemApi + @TestApi + public static class Sensors { + + private Sensors() {} + + /** Microphone + * @hide */ + @SystemApi + @TestApi + public static final int MICROPHONE = SensorPrivacyIndividualEnabledSensorProto.MICROPHONE; + /** Camera + * @hide */ + @SystemApi + @TestApi + public static final int CAMERA = SensorPrivacyIndividualEnabledSensorProto.CAMERA; + + /** + * Individual sensors not listed in {@link Sensors} + * + * @hide + */ + @IntDef(value = { + MICROPHONE, + CAMERA + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Sensor {} + } /** * A class implementing this interface can register with the {@link @@ -88,6 +113,8 @@ public final class SensorPrivacyManager { * * @hide */ + @SystemApi + @TestApi public interface OnSensorPrivacyChangedListener { /** * Callback invoked when the sensor privacy state changes. @@ -165,7 +192,8 @@ public final class SensorPrivacyManager { * * @hide */ - public void addSensorPrivacyListener(final OnSensorPrivacyChangedListener listener) { + @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) + public void addSensorPrivacyListener(@NonNull final OnSensorPrivacyChangedListener listener) { synchronized (mListeners) { ISensorPrivacyListener iListener = mListeners.get(listener); if (iListener == null) { @@ -196,15 +224,37 @@ public final class SensorPrivacyManager { * * @hide */ - public void addSensorPrivacyListener(@IndividualSensor int sensor, - final OnSensorPrivacyChangedListener listener) { + @SystemApi + @TestApi + @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) + public void addSensorPrivacyListener(@Sensors.Sensor int sensor, + @NonNull OnSensorPrivacyChangedListener listener) { + addSensorPrivacyListener(sensor, mContext.getMainExecutor(), listener); + } + + /** + * Registers a new listener to receive notification when the state of sensor privacy + * changes. + * + * @param sensor the sensor to listen to changes to + * @param executor the executor to dispatch the callback on + * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor + * privacy changes. + * + * @hide + */ + @SystemApi + @TestApi + @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) + public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @NonNull Executor executor, + @NonNull OnSensorPrivacyChangedListener listener) { synchronized (mListeners) { ISensorPrivacyListener iListener = mListeners.get(listener); if (iListener == null) { iListener = new ISensorPrivacyListener.Stub() { @Override public void onSensorPrivacyChanged(boolean enabled) { - listener.onSensorPrivacyChanged(enabled); + executor.execute(() -> listener.onSensorPrivacyChanged(enabled)); } }; mListeners.put(listener, iListener); @@ -228,7 +278,10 @@ public final class SensorPrivacyManager { * * @hide */ - public void removeSensorPrivacyListener(OnSensorPrivacyChangedListener listener) { + @SystemApi + @TestApi + @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) + public void removeSensorPrivacyListener(@NonNull OnSensorPrivacyChangedListener listener) { synchronized (mListeners) { ISensorPrivacyListener iListener = mListeners.get(listener); if (iListener != null) { @@ -249,6 +302,7 @@ public final class SensorPrivacyManager { * * @hide */ + @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled() { try { return mService.isSensorPrivacyEnabled(); @@ -264,8 +318,10 @@ public final class SensorPrivacyManager { * * @hide */ + @SystemApi @TestApi - public boolean isIndividualSensorPrivacyEnabled(@IndividualSensor int sensor) { + @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) + public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor) { try { return mService.isIndividualSensorPrivacyEnabled(mContext.getUserId(), sensor); } catch (RemoteException e) { @@ -283,8 +339,7 @@ public final class SensorPrivacyManager { */ @TestApi @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) - public void setIndividualSensorPrivacy(@IndividualSensor int sensor, - boolean enable) { + public void setSensorPrivacy(@Sensors.Sensor int sensor, boolean enable) { try { mService.setIndividualSensorPrivacy(mContext.getUserId(), sensor, enable); } catch (RemoteException e) { @@ -303,7 +358,7 @@ public final class SensorPrivacyManager { */ @TestApi @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) - public void setIndividualSensorPrivacyForProfileGroup(@IndividualSensor int sensor, + public void setSensorPrivacyForProfileGroup(@Sensors.Sensor int sensor, boolean enable) { try { mService.setIndividualSensorPrivacyForProfileGroup(mContext.getUserId(), sensor, @@ -321,7 +376,8 @@ public final class SensorPrivacyManager { * * @hide */ - public void suppressIndividualSensorPrivacyReminders(@NonNull String packageName, + @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) + public void suppressSensorPrivacyReminders(@NonNull String packageName, boolean suppress) { try { mService.suppressIndividualSensorPrivacyReminders(mContext.getUserId(), packageName, diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index f66ecdfb79c3..0256b7bc6de0 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -1392,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: @@ -1448,13 +1448,17 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan case FACE_ACQUIRED_TOO_FAR: return context.getString(R.string.face_acquired_too_far); case FACE_ACQUIRED_TOO_HIGH: - return context.getString(R.string.face_acquired_too_high); - case FACE_ACQUIRED_TOO_LOW: + // TODO(b/181269243): Change back once error codes are fixed. return context.getString(R.string.face_acquired_too_low); + case FACE_ACQUIRED_TOO_LOW: + // TODO(b/181269243) Change back once error codes are fixed. + return context.getString(R.string.face_acquired_too_high); case FACE_ACQUIRED_TOO_RIGHT: - return context.getString(R.string.face_acquired_too_right); - case FACE_ACQUIRED_TOO_LEFT: + // TODO(b/181269243) Change back once error codes are fixed. return context.getString(R.string.face_acquired_too_left); + case FACE_ACQUIRED_TOO_LEFT: + // TODO(b/181269243) Change back once error codes are fixed. + return context.getString(R.string.face_acquired_too_right); case FACE_ACQUIRED_POOR_GAZE: return context.getString(R.string.face_acquired_poor_gaze); case FACE_ACQUIRED_NOT_DETECTED: diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java index c6efaace76d7..2b6f336848c3 100644 --- a/core/java/android/os/BatteryConsumer.java +++ b/core/java/android/os/BatteryConsumer.java @@ -47,6 +47,7 @@ public abstract class BatteryConsumer { POWER_COMPONENT_SYSTEM_SERVICES, POWER_COMPONENT_SENSORS, POWER_COMPONENT_GNSS, + POWER_COMPONENT_WIFI, POWER_COMPONENT_WAKELOCK, POWER_COMPONENT_SCREEN, POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS, @@ -66,6 +67,7 @@ public abstract class BatteryConsumer { public static final int POWER_COMPONENT_MOBILE_RADIO = 8; public static final int POWER_COMPONENT_SENSORS = 9; public static final int POWER_COMPONENT_GNSS = 10; + public static final int POWER_COMPONENT_WIFI = 11; public static final int POWER_COMPONENT_WAKELOCK = 12; public static final int POWER_COMPONENT_SCREEN = 13; // Power that is re-attributed to other battery consumers. For example, for System Server @@ -94,6 +96,7 @@ public abstract class BatteryConsumer { TIME_COMPONENT_MOBILE_RADIO, TIME_COMPONENT_SENSORS, TIME_COMPONENT_GNSS, + TIME_COMPONENT_WIFI, TIME_COMPONENT_WAKELOCK, TIME_COMPONENT_SCREEN, }) @@ -112,6 +115,7 @@ public abstract class BatteryConsumer { public static final int TIME_COMPONENT_MOBILE_RADIO = 8; public static final int TIME_COMPONENT_SENSORS = 9; public static final int TIME_COMPONENT_GNSS = 10; + public static final int TIME_COMPONENT_WIFI = 11; public static final int TIME_COMPONENT_WAKELOCK = 12; public static final int TIME_COMPONENT_SCREEN = 13; diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java index bb40d905d481..dfa0c396485d 100644 --- a/core/java/android/os/UidBatteryConsumer.java +++ b/core/java/android/os/UidBatteryConsumer.java @@ -16,9 +16,13 @@ package android.os; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Contains power consumption data attributed to a specific UID. * @@ -26,9 +30,37 @@ import android.annotation.Nullable; */ public final class UidBatteryConsumer extends BatteryConsumer implements Parcelable { + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + STATE_FOREGROUND, + STATE_BACKGROUND + }) + public @interface State { + } + + /** + * The state of an application when it is either running a foreground (top) activity + * or a foreground service. + */ + public static final int STATE_FOREGROUND = 0; + + /** + * The state of an application when it is running in the background, including the following + * states: + * + * {@link android.app.ActivityManager#PROCESS_STATE_IMPORTANT_BACKGROUND}, + * {@link android.app.ActivityManager#PROCESS_STATE_TRANSIENT_BACKGROUND}, + * {@link android.app.ActivityManager#PROCESS_STATE_BACKUP}, + * {@link android.app.ActivityManager#PROCESS_STATE_SERVICE}, + * {@link android.app.ActivityManager#PROCESS_STATE_RECEIVER}. + */ + public static final int STATE_BACKGROUND = 1; + private final int mUid; @Nullable private final String mPackageWithHighestDrain; + private final long mTimeInForegroundMs; + private final long mTimeInBackgroundMs; public int getUid() { return mUid; @@ -39,16 +71,33 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela return mPackageWithHighestDrain; } + /** + * Returns the amount of time in milliseconds this UID spent in the specified state. + */ + public long getTimeInStateMs(@State int state) { + switch (state) { + case STATE_BACKGROUND: + return mTimeInBackgroundMs; + case STATE_FOREGROUND: + return mTimeInForegroundMs; + } + return 0; + } + private UidBatteryConsumer(@NonNull Builder builder) { super(builder.mPowerComponentsBuilder.build()); mUid = builder.mUid; mPackageWithHighestDrain = builder.mPackageWithHighestDrain; + mTimeInForegroundMs = builder.mTimeInForegroundMs; + mTimeInBackgroundMs = builder.mTimeInBackgroundMs; } private UidBatteryConsumer(@NonNull Parcel source) { super(new PowerComponents(source)); mUid = source.readInt(); mPackageWithHighestDrain = source.readString(); + mTimeInForegroundMs = source.readLong(); + mTimeInBackgroundMs = source.readLong(); } /** @@ -59,6 +108,8 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela super.writeToParcel(dest, flags); dest.writeInt(mUid); dest.writeString(mPackageWithHighestDrain); + dest.writeLong(mTimeInForegroundMs); + dest.writeLong(mTimeInBackgroundMs); } @NonNull @@ -84,6 +135,8 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela private final BatteryStats.Uid mBatteryStatsUid; private final int mUid; private String mPackageWithHighestDrain; + public long mTimeInForegroundMs; + public long mTimeInBackgroundMs; private boolean mExcludeFromBatteryUsageStats; public Builder(int customPowerComponentCount, int customTimeComponentCount, @@ -113,6 +166,25 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela } /** + * Sets the duration, in milliseconds, that this UID was active in a particular state, + * such as foreground or background. + */ + @NonNull + public Builder setTimeInStateMs(@State int state, long timeInStateMs) { + switch (state) { + case STATE_FOREGROUND: + mTimeInForegroundMs = timeInStateMs; + break; + case STATE_BACKGROUND: + mTimeInBackgroundMs = timeInStateMs; + break; + default: + throw new IllegalArgumentException("Unsupported state: " + state); + } + return this; + } + + /** * Marks the UidBatteryConsumer for exclusion from the result set. */ public Builder excludeFromBatteryUsageStats() { diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 8bdfd3d3d627..682754e66904 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1688,6 +1688,7 @@ public class UserManager { * @return Whether guest user is always ephemeral * @hide */ + @TestApi public static boolean isGuestUserEphemeral() { return Resources.getSystem() .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral); @@ -1802,6 +1803,20 @@ public class UserManager { } /** + * @return the user type of the context user. + * @hide + */ + @TestApi + @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS + }) + @UserHandleAware + public @NonNull String getUserType() { + UserInfo userInfo = getUserInfo(mUserId); + return userInfo == null ? "" : userInfo.userType; + } + + /** * Returns the user name of the context user. This call is only available to applications on * the system image; it requires the {@code android.permission.MANAGE_USERS} or {@code * android.permission.GET_ACCOUNTS_PRIVILEGED} permissions. @@ -1809,7 +1824,8 @@ public class UserManager { * @return the user name */ @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, - android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}, conditional = true) + android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED, + android.Manifest.permission.CREATE_USERS}, conditional = true) @UserHandleAware public @NonNull String getUserName() { if (UserHandle.myUserId() == mUserId) { @@ -2792,6 +2808,7 @@ public class UserManager { */ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, Manifest.permission.CREATE_USERS}) + @TestApi public @Nullable UserInfo createUser(@Nullable String name, @NonNull String userType, @UserInfoFlag int flags) { try { @@ -2828,6 +2845,7 @@ public class UserManager { * @throws UserOperationException if the user could not be created. * @hide */ + @TestApi @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, Manifest.permission.CREATE_USERS}) public @NonNull UserInfo preCreateUser(@NonNull String userType) @@ -2976,10 +2994,11 @@ public class UserManager { * * @hide */ + @TestApi @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, Manifest.permission.CREATE_USERS}) - public UserInfo createProfileForUser(String name, @NonNull String userType, - @UserInfoFlag int flags, @UserIdInt int userId, String[] disallowedPackages) { + public @Nullable UserInfo createProfileForUser(@Nullable String name, @NonNull String userType, + @UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages) { try { return mService.createProfileForUserWithThrow(name, userType, flags, userId, disallowedPackages); @@ -3022,9 +3041,10 @@ public class UserManager { * * @hide */ + @TestApi @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, Manifest.permission.CREATE_USERS}) - public UserInfo createRestrictedProfile(String name) { + public @Nullable UserInfo createRestrictedProfile(@Nullable String name) { try { UserHandle parentUserHandle = Process.myUserHandle(); UserInfo user = mService.createRestrictedProfileWithThrow(name, @@ -3248,10 +3268,11 @@ public class UserManager { /** * Return the number of users currently created on the device. - * <p>This API is not for use by third-party apps. It requires the {@code MANAGE_USERS} - * permission.</p> */ - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS + }) public int getUserCount() { List<UserInfo> users = getUsers(); return users != null ? users.size() : 1; @@ -3274,7 +3295,10 @@ public class UserManager { * @hide */ @UnsupportedAppUsage - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS + }) public List<UserInfo> getUsers() { return getUsers(/*excludePartial= */ true, /* excludeDying= */ false, /* excludePreCreated= */ true); @@ -3292,7 +3316,10 @@ public class UserManager { * @return the list of users that were created. * @hide */ - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS + }) public @NonNull List<UserInfo> getAliveUsers() { return getUsers(/*excludePartial= */ true, /* excludeDying= */ true, /* excludePreCreated= */ true); @@ -3306,7 +3333,10 @@ public class UserManager { */ @Deprecated @UnsupportedAppUsage - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS + }) public @NonNull List<UserInfo> getUsers(boolean excludeDying) { return getUsers(/*excludePartial= */ true, excludeDying, /* excludePreCreated= */ true); @@ -3317,8 +3347,12 @@ public class UserManager { * * @hide */ - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) - public List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying, + @TestApi + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS + }) + public @NonNull List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying, boolean excludePreCreated) { try { return mService.getUsers(excludePartial, excludeDying, excludePreCreated); @@ -3335,7 +3369,10 @@ public class UserManager { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS + }) public @NonNull List<UserHandle> getUserHandles(boolean excludeDying) { List<UserInfo> users = getUsers(/* excludePartial= */ true, excludeDying, /* excludePreCreated= */ true); @@ -3354,7 +3391,10 @@ public class UserManager { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS + }) public long[] getSerialNumbersOfUsers(boolean excludeDying) { List<UserInfo> users = getUsers(/* excludePartial= */ true, excludeDying, /* excludePreCreated= */ true); @@ -3678,7 +3718,10 @@ public class UserManager { * @hide */ @UnsupportedAppUsage - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS + }) public UserInfo getProfileParent(@UserIdInt int userId) { try { return mService.getProfileParent(userId); @@ -3697,7 +3740,10 @@ public class UserManager { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS + }) public @Nullable UserHandle getProfileParent(@NonNull UserHandle user) { UserInfo info = getProfileParent(user.getIdentifier()); diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java index cec6a1fb271d..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; } /** 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 4354920c83ec..6e89faf9c2ed 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -157,6 +157,14 @@ public final class DeviceConfig { public static final String NAMESPACE_BLUETOOTH = "bluetooth"; /** + * Namespace for features relating to clipboard. + * + * @hide + */ + @SystemApi + public static final String NAMESPACE_CLIPBOARD = "clipboard"; + + /** * Namespace for all networking connectivity related features. * * @hide diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 345cc84e8593..09d0af11a39f 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -14622,6 +14622,29 @@ 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/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index ffb4a6eeb1ab..3c355d4b6f45 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -270,13 +270,13 @@ public class TelephonyRegistryManager { /** * Notify call state changed on certain subscription. * - * @param subId for which call state changed. * @param slotIndex for which call state changed. Can be derived from subId except when subId is * invalid. + * @param subId for which call state changed. * @param state latest call state. e.g, offhook, ringing * @param incomingNumber incoming phone number. */ - public void notifyCallStateChanged(int subId, int slotIndex, @CallState int state, + public void notifyCallStateChanged(int slotIndex, int subId, @CallState int state, @Nullable String incomingNumber) { try { sRegistry.notifyCallState(slotIndex, subId, state, incomingNumber); @@ -329,12 +329,12 @@ public class TelephonyRegistryManager { /** * Notify {@link ServiceState} update on certain subscription. * - * @param subId for which the service state changed. * @param slotIndex for which the service state changed. Can be derived from subId except * subId is invalid. + * @param subId for which the service state changed. * @param state service state e.g, in service, out of service or roaming status. */ - public void notifyServiceStateChanged(int subId, int slotIndex, @NonNull ServiceState state) { + public void notifyServiceStateChanged(int slotIndex, int subId, @NonNull ServiceState state) { try { sRegistry.notifyServiceStateForPhoneId(slotIndex, subId, state); } catch (RemoteException ex) { @@ -345,12 +345,12 @@ public class TelephonyRegistryManager { /** * Notify {@link SignalStrength} update on certain subscription. * - * @param subId for which the signalstrength changed. * @param slotIndex for which the signalstrength changed. Can be derived from subId except when * subId is invalid. + * @param subId for which the signalstrength changed. * @param signalStrength e.g, signalstrength level {@see SignalStrength#getLevel()} */ - public void notifySignalStrengthChanged(int subId, int slotIndex, + public void notifySignalStrengthChanged(int slotIndex, int subId, @NonNull SignalStrength signalStrength) { try { sRegistry.notifySignalStrengthForPhoneId(slotIndex, subId, signalStrength); @@ -363,13 +363,13 @@ public class TelephonyRegistryManager { * Notify changes to the message-waiting indicator on certain subscription. e.g, The status bar * uses message waiting indicator to determine when to display the voicemail icon. * - * @param subId for which message waiting indicator changed. * @param slotIndex for which message waiting indicator changed. Can be derived from subId * except when subId is invalid. + * @param subId for which message waiting indicator changed. * @param msgWaitingInd {@code true} indicates there is message-waiting indicator, {@code false} * otherwise. */ - public void notifyMessageWaitingChanged(int subId, int slotIndex, boolean msgWaitingInd) { + public void notifyMessageWaitingChanged(int slotIndex, int subId, boolean msgWaitingInd) { try { sRegistry.notifyMessageWaitingChangedForPhoneId(slotIndex, subId, msgWaitingInd); } catch (RemoteException ex) { @@ -410,9 +410,9 @@ public class TelephonyRegistryManager { /** * Notify changes to default (Internet) data connection state on certain subscription. * - * @param subId for which data connection state changed. * @param slotIndex for which data connections state changed. Can be derived from subId except * when subId is invalid. + * @param subId for which data connection state changed. * @param preciseState the PreciseDataConnectionState * * @see PreciseDataConnectionState @@ -431,13 +431,13 @@ public class TelephonyRegistryManager { /** * Notify {@link CallQuality} change on certain subscription. * - * @param subId for which call quality state changed. * @param slotIndex for which call quality state changed. Can be derived from subId except when * subId is invalid. + * @param subId for which call quality state changed. * @param callQuality Information about call quality e.g, call quality level * @param networkType associated with this data connection. e.g, LTE */ - public void notifyCallQualityChanged(int subId, int slotIndex, @NonNull CallQuality callQuality, + public void notifyCallQualityChanged(int slotIndex, int subId, @NonNull CallQuality callQuality, @NetworkType int networkType) { try { sRegistry.notifyCallQualityChanged(callQuality, slotIndex, subId, networkType); @@ -449,11 +449,11 @@ public class TelephonyRegistryManager { /** * Notify emergency number list changed on certain subscription. * - * @param subId for which emergency number list changed. * @param slotIndex for which emergency number list changed. Can be derived from subId except * when subId is invalid. + * @param subId for which emergency number list changed. */ - public void notifyEmergencyNumberList(int subId, int slotIndex) { + public void notifyEmergencyNumberList( int slotIndex, int subId) { try { sRegistry.notifyEmergencyNumberList(slotIndex, subId); } catch (RemoteException ex) { @@ -494,13 +494,13 @@ public class TelephonyRegistryManager { /** * Notify radio power state changed on certain subscription. * - * @param subId for which radio power state changed. * @param slotIndex for which radio power state changed. Can be derived from subId except when * subId is invalid. + * @param subId for which radio power state changed. * @param radioPowerState the current modem radio state. */ - public void notifyRadioPowerStateChanged(int subId, int slotIndex, - @RadioPowerState int radioPowerState) { + public void notifyRadioPowerStateChanged(int slotIndex, int subId, + @RadioPowerState int radioPowerState) { try { sRegistry.notifyRadioPowerStateChanged(slotIndex, subId, radioPowerState); } catch (RemoteException ex) { @@ -538,13 +538,13 @@ public class TelephonyRegistryManager { * Notify data activation state changed on certain subscription. * @see TelephonyManager#getDataActivationState() * - * @param subId for which data activation state changed. * @param slotIndex for which data activation state changed. Can be derived from subId except * when subId is invalid. + * @param subId for which data activation state changed. * @param activationState sim activation state e.g, activated. */ - public void notifyDataActivationStateChanged(int subId, int slotIndex, - @SimActivationState int activationState) { + public void notifyDataActivationStateChanged(int slotIndex, int subId, + @SimActivationState int activationState) { try { sRegistry.notifySimActivationStateChangedForPhoneId(slotIndex, subId, SIM_ACTIVATION_TYPE_DATA, activationState); @@ -557,13 +557,13 @@ public class TelephonyRegistryManager { * Notify voice activation state changed on certain subscription. * @see TelephonyManager#getVoiceActivationState() * - * @param subId for which voice activation state changed. * @param slotIndex for which voice activation state changed. Can be derived from subId except * subId is invalid. + * @param subId for which voice activation state changed. * @param activationState sim activation state e.g, activated. */ - public void notifyVoiceActivationStateChanged(int subId, int slotIndex, - @SimActivationState int activationState) { + public void notifyVoiceActivationStateChanged(int slotIndex, int subId, + @SimActivationState int activationState) { try { sRegistry.notifySimActivationStateChangedForPhoneId(slotIndex, subId, SIM_ACTIVATION_TYPE_VOICE, activationState); @@ -576,9 +576,9 @@ public class TelephonyRegistryManager { * Notify User mobile data state changed on certain subscription. e.g, mobile data is enabled * or disabled. * - * @param subId for which mobile data state has changed. * @param slotIndex for which mobile data state has changed. Can be derived from subId except * when subId is invalid. + * @param subId for which mobile data state has changed. * @param state {@code true} indicates mobile data is enabled/on. {@code false} otherwise. */ public void notifyUserMobileDataStateChanged(int slotIndex, int subId, boolean state) { @@ -599,7 +599,7 @@ public class TelephonyRegistryManager { * @param telephonyDisplayInfo The display info. */ public void notifyDisplayInfoChanged(int slotIndex, int subscriptionId, - @NonNull TelephonyDisplayInfo telephonyDisplayInfo) { + @NonNull TelephonyDisplayInfo telephonyDisplayInfo) { try { sRegistry.notifyDisplayInfoChanged(slotIndex, subscriptionId, telephonyDisplayInfo); } catch (RemoteException ex) { @@ -640,14 +640,14 @@ public class TelephonyRegistryManager { * Notify precise call state changed on certain subscription, including foreground, background * and ringcall states. * - * @param subId for which precise call state changed. * @param slotIndex for which precise call state changed. Can be derived from subId except when * subId is invalid. + * @param subId for which precise call state changed. * @param ringCallPreciseState ringCall state. * @param foregroundCallPreciseState foreground call state. * @param backgroundCallPreciseState background call state. */ - public void notifyPreciseCallState(int subId, int slotIndex, + public void notifyPreciseCallState(int slotIndex, int subId, @PreciseCallStates int ringCallPreciseState, @PreciseCallStates int foregroundCallPreciseState, @PreciseCallStates int backgroundCallPreciseState) { @@ -790,9 +790,10 @@ public class TelephonyRegistryManager { * @param reason Reason for data enabled/disabled. See {@code REASON_*} in * {@link TelephonyManager}. */ - public void notifyDataEnabled(boolean enabled, @TelephonyManager.DataEnabledReason int reason) { + public void notifyDataEnabled(int slotIndex, int subId, boolean enabled, + @TelephonyManager.DataEnabledReason int reason) { try { - sRegistry.notifyDataEnabled(enabled, reason); + sRegistry.notifyDataEnabled(slotIndex, subId, enabled, reason); } catch (RemoteException ex) { // system server crash } @@ -801,11 +802,11 @@ public class TelephonyRegistryManager { /** * Notify emergency number list changed on certain subscription. * - * @param subId for which emergency number list changed. * @param slotIndex for which emergency number list changed. Can be derived from subId except * when subId is invalid. + * @param subId for which emergency number list changed. */ - public void notifyAllowedNetworkTypesChanged(int subId, int slotIndex, + public void notifyAllowedNetworkTypesChanged(int slotIndex, int subId, Map<Integer, Long> allowedNetworkTypeList) { try { sRegistry.notifyAllowedNetworkTypesChanged(slotIndex, subId, allowedNetworkTypeList); diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java index aef185c77633..33b7c757f1e8 100644 --- a/core/java/android/text/FontConfig.java +++ b/core/java/android/text/FontConfig.java @@ -18,6 +18,7 @@ package android.text; import static java.lang.annotation.RetentionPolicy.SOURCE; +import android.annotation.CurrentTimeMillisLong; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -101,7 +102,7 @@ public final class FontConfig implements Parcelable { * * If there is no update, this return 0. */ - public long getLastModifiedTimeMillis() { + public @CurrentTimeMillisLong long getLastModifiedTimeMillis() { return mLastModifiedTimeMillis; } 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/MergedConfiguration.java b/core/java/android/util/MergedConfiguration.java index 3ac44aacc2c7..4328e0826bd0 100644 --- a/core/java/android/util/MergedConfiguration.java +++ b/core/java/android/util/MergedConfiguration.java @@ -33,9 +33,9 @@ import java.io.PrintWriter; */ public class MergedConfiguration implements Parcelable { - private Configuration mGlobalConfig = new Configuration(); - private Configuration mOverrideConfig = new Configuration(); - private Configuration mMergedConfig = new Configuration(); + private final Configuration mGlobalConfig = new Configuration(); + private final Configuration mOverrideConfig = new Configuration(); + private final Configuration mMergedConfig = new Configuration(); public MergedConfiguration() { } @@ -59,15 +59,15 @@ public class MergedConfiguration implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeParcelable(mGlobalConfig, flags); - dest.writeParcelable(mOverrideConfig, flags); - dest.writeParcelable(mMergedConfig, flags); + mGlobalConfig.writeToParcel(dest, flags); + mOverrideConfig.writeToParcel(dest, flags); + mMergedConfig.writeToParcel(dest, flags); } public void readFromParcel(Parcel source) { - mGlobalConfig = source.readParcelable(Configuration.class.getClassLoader()); - mOverrideConfig = source.readParcelable(Configuration.class.getClassLoader()); - mMergedConfig = source.readParcelable(Configuration.class.getClassLoader()); + mGlobalConfig.readFromParcel(source); + mOverrideConfig.readFromParcel(source); + mMergedConfig.readFromParcel(source); } @Override 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/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/IScrollCaptureCallbacks.aidl b/core/java/android/view/IScrollCaptureCallbacks.aidl index d97e3c66cc5d..26eaac0a2bf5 100644 --- a/core/java/android/view/IScrollCaptureCallbacks.aidl +++ b/core/java/android/view/IScrollCaptureCallbacks.aidl @@ -16,12 +16,10 @@ package android.view; -import android.graphics.Point; import android.graphics.Rect; +import android.view.ScrollCaptureResponse; import android.view.Surface; -import android.view.IScrollCaptureConnection; - /** * Asynchronous callback channel for responses to scroll capture requests. * @@ -29,34 +27,30 @@ import android.view.IScrollCaptureConnection; */ interface IScrollCaptureCallbacks { /** - * Scroll capture is available, and a connection has been provided. + * Provides the result of WindowManagerService#requestScrollCapture * - * @param connection a connection to a window process and scrollable content - * @param scrollAreaInWindow the location of scrolling in global (window) coordinate space + * @param response the response which describes the result */ - oneway void onConnected(in IScrollCaptureConnection connection, in Rect scrollBounds, - in Point positionInWindow); + oneway void onScrollCaptureResponse(in ScrollCaptureResponse response); /** - * The window does not support scroll capture. - */ - oneway void onUnavailable(); - - /** - * Called when the remote end has confirmed the request and is ready to begin providing image - * requests. + * Called in reply to IScrollCaptureConnection#startCapture, when the remote end has confirmed + * the request and is ready to begin capturing images. */ oneway void onCaptureStarted(); /** - * Received a response from a capture request. + * Received a response from a capture request. The provided rectangle indicates the portion + * of the requested rectangle which was captured. An empty rectangle indicates that the request + * could not be satisfied (most commonly due to the available scrolling range). + * + * @param flags flags describing additional status of the result + * @param capturedArea the actual area of the image captured */ - oneway void onCaptureBufferSent(long frameNumber, in Rect capturedArea); + oneway void onImageRequestCompleted(int flags, in Rect capturedArea); /** - * Signals that the capture session has completed and the target window may be returned to - * normal interactive use. This may be due to normal shutdown, or after a timeout or other - * unrecoverable state change such as activity lifecycle, window visibility or focus. + * Signals that the capture session has completed and the target window is ready for normal use. */ - oneway void onConnectionClosed(); + oneway void onCaptureEnded(); } diff --git a/core/java/android/view/IScrollCaptureConnection.aidl b/core/java/android/view/IScrollCaptureConnection.aidl index 63a4f48aeb20..c55e88800393 100644 --- a/core/java/android/view/IScrollCaptureConnection.aidl +++ b/core/java/android/view/IScrollCaptureConnection.aidl @@ -17,33 +17,45 @@ package android.view; import android.graphics.Rect; +import android.os.ICancellationSignal; import android.view.Surface; /** - * Interface implemented by a client of the Scroll Capture framework to receive requests - * to start, capture images and end the session. + * A remote connection to a scroll capture target. * * {@hide} */ interface IScrollCaptureConnection { /** - * Informs the client that it has been selected for scroll capture and should prepare to - * to begin handling capture requests. + * Informs the target that it has been selected for scroll capture. + * + * @param surface a return channel for image buffers + * + * @return a cancallation signal which is used cancel the request + */ + ICancellationSignal startCapture(in Surface surface); + + /** + * Request the target capture an image within the provided rectangle. + * + * @param surface a return channel for image buffers + * @param signal a cancallation signal which can interrupt the request + * + * @return a cancallation signal which is used cancel the request */ - oneway void startCapture(in Surface surface); + ICancellationSignal requestImage(in Rect captureArea); /** - * Request the client capture an image within the provided rectangle. + * Inform the target that capture has ended. * - * @see android.view.ScrollCaptureCallback#onScrollCaptureRequest + * @return a cancallation signal which is used cancel the request */ - oneway void requestImage(in Rect captureArea); + ICancellationSignal endCapture(); /** - * Inform the client that capture has ended. The client should shut down and release all - * local resources in use and prepare for return to normal interactive usage. + * Closes the connection. */ - oneway void endCapture(); + oneway void close(); } 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/InsetsSource.java b/core/java/android/view/InsetsSource.java index 5f2bccc8b857..e6cf68367ae6 100644 --- a/core/java/android/view/InsetsSource.java +++ b/core/java/android/view/InsetsSource.java @@ -261,11 +261,7 @@ public class InsetsSource implements Parcelable { public InsetsSource(Parcel in) { mType = in.readInt(); - if (in.readInt() != 0) { - mFrame = Rect.CREATOR.createFromParcel(in); - } else { - mFrame = null; - } + mFrame = Rect.CREATOR.createFromParcel(in); if (in.readInt() != 0) { mVisibleFrame = Rect.CREATOR.createFromParcel(in); } else { @@ -282,12 +278,7 @@ public class InsetsSource implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mType); - if (mFrame != null) { - dest.writeInt(1); - mFrame.writeToParcel(dest, 0); - } else { - dest.writeInt(0); - } + mFrame.writeToParcel(dest, 0); if (mVisibleFrame != null) { dest.writeInt(1); mVisibleFrame.writeToParcel(dest, 0); diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java index c717c2adc32d..1d4b4111f884 100644 --- a/core/java/android/view/InsetsSourceControl.java +++ b/core/java/android/view/InsetsSourceControl.java @@ -77,8 +77,8 @@ public class InsetsSourceControl implements Parcelable { public InsetsSourceControl(Parcel in) { mType = in.readInt(); - mLeash = in.readParcelable(null /* loader */); - mSurfacePosition = in.readParcelable(null /* loader */); + mLeash = in.readTypedObject(SurfaceControl.CREATOR); + mSurfacePosition = in.readTypedObject(Point.CREATOR); mSkipAnimationOnce = in.readBoolean(); } @@ -119,8 +119,8 @@ public class InsetsSourceControl implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mType); - dest.writeParcelable(mLeash, 0 /* flags*/); - dest.writeParcelable(mSurfacePosition, 0 /* flags*/); + dest.writeTypedObject(mLeash, 0 /* parcelableFlags */); + dest.writeTypedObject(mSurfacePosition, 0 /* parcelableFlags */); dest.writeBoolean(mSkipAnimationOnce); } diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 21dd1fb05615..fce1952de44b 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -156,7 +156,7 @@ public class InsetsState implements Parcelable { static final int ISIDE_FLOATING = 4; static final int ISIDE_UNKNOWN = 5; - private InsetsSource[] mSources = new InsetsSource[SIZE]; + private final InsetsSource[] mSources = new InsetsSource[SIZE]; /** * The frame of the display these sources are relative to. @@ -804,7 +804,7 @@ public class InsetsState implements Parcelable { public void writeToParcel(Parcel dest, int flags) { mDisplayFrame.writeToParcel(dest, flags); mDisplayCutout.writeToParcel(dest, flags); - dest.writeParcelableArray(mSources, 0); + dest.writeTypedArray(mSources, 0 /* parcelableFlags */); dest.writeTypedObject(mRoundedCorners, flags); } @@ -820,9 +820,9 @@ public class InsetsState implements Parcelable { }; public void readFromParcel(Parcel in) { - mDisplayFrame.set(Rect.CREATOR.createFromParcel(in)); - mDisplayCutout.set(DisplayCutout.ParcelableWrapper.CREATOR.createFromParcel(in)); - mSources = in.readParcelableArray(null, InsetsSource.class); + mDisplayFrame.readFromParcel(in); + mDisplayCutout.readFromParcel(in); + in.readTypedArray(mSources, InsetsSource.CREATOR); mRoundedCorners = in.readTypedObject(RoundedCorners.CREATOR); } diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS index e66b17aa4426..c43c410ab995 100644 --- a/core/java/android/view/OWNERS +++ b/core/java/android/view/OWNERS @@ -77,3 +77,7 @@ per-file SyncRtSurfaceTransactionApplier.java = file:/services/core/java/com/and per-file ViewRootInsetsControllerHost.java = file:/services/core/java/com/android/server/wm/OWNERS per-file Window*.java = file:/services/core/java/com/android/server/wm/OWNERS per-file Window*.aidl = file:/services/core/java/com/android/server/wm/OWNERS + +# Scroll Capture +per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS +per-file *CaptureHelper*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS diff --git a/core/java/android/view/ScrollCaptureCallback.java b/core/java/android/view/ScrollCaptureCallback.java index d3aad2c72d27..16886160baca 100644 --- a/core/java/android/view/ScrollCaptureCallback.java +++ b/core/java/android/view/ScrollCaptureCallback.java @@ -19,6 +19,7 @@ package android.view; import android.annotation.NonNull; import android.annotation.UiThread; import android.graphics.Rect; +import android.os.CancellationSignal; import java.util.function.Consumer; @@ -58,8 +59,6 @@ import java.util.function.Consumer; * @see View#setScrollCaptureHint(int) * @see View#setScrollCaptureCallback(ScrollCaptureCallback) * @see Window#registerScrollCaptureCallback(ScrollCaptureCallback) - * - * @hide */ @UiThread public interface ScrollCaptureCallback { @@ -68,80 +67,84 @@ public interface ScrollCaptureCallback { * The system is searching for the appropriate scrolling container to capture and would like to * know the size and position of scrolling content handled by this callback. * <p> - * Implementations should inset {@code containingViewBounds} to cover only the area within the - * containing view where scrolling content may be positioned. This should cover only the content - * which tracks with scrolling movement. - * <p> - * Return the updated rectangle to {@code resultConsumer}. If for any reason the scrolling - * content is not available to capture, a {@code null} rectangle may be returned, and this view - * will be excluded as the target for this request. + * To determine scroll bounds, an implementation should inset the visible bounds of the + * containing view to cover only the area where scrolling content may be positioned. This + * should cover only the content which tracks with scrolling movement. * <p> - * Responses received after XXXms will be discarded. + * Return the updated rectangle to {@link Consumer#accept onReady.accept}. If for any reason the + * scrolling content is not available to capture, a empty rectangle may be returned which will + * exclude this view from consideration. * <p> - * TODO: finalize timeout + * This request may be cancelled via the provided {@link CancellationSignal}. When this happens, + * any future call to {@link Consumer#accept onReady.accept} will have no effect and this + * content will be omitted from the search results. * - * @param onReady consumer for the updated rectangle + * @param signal signal to cancel the operation in progress + * @param onReady consumer for the updated rectangle */ - void onScrollCaptureSearch(@NonNull Consumer<Rect> onReady); + void onScrollCaptureSearch(@NonNull CancellationSignal signal, @NonNull Consumer<Rect> onReady); /** * Scroll Capture has selected this callback to provide the scrolling image content. * <p> - * The onReady signal should be called when ready to begin handling image requests. + * {@link Runnable#run onReady.run} should be called when ready to begin handling image + * requests. + * <p> + * This request may be cancelled via the provided {@link CancellationSignal}. When this happens, + * any future call to {@link Runnable#run onReady.run} will have no effect and provided session + * will not be activated. + * + * @param session the current session, resources provided by it are valid for use until the + * {@link #onScrollCaptureEnd(Runnable) session ends} + * @param signal signal to cancel the operation in progress + * @param onReady signal used to report completion of the request */ - void onScrollCaptureStart(@NonNull ScrollCaptureSession session, @NonNull Runnable onReady); + void onScrollCaptureStart(@NonNull ScrollCaptureSession session, + @NonNull CancellationSignal signal, @NonNull Runnable onReady); /** * An image capture has been requested from the scrolling content. * <p> - * <code>captureArea</code> contains the bounds of the image requested, relative to the - * rectangle provided by {@link ScrollCaptureCallback#onScrollCaptureSearch}, referred to as - * {@code scrollBounds}. - * here. - * <p> - * A series of requests will step by a constant vertical amount relative to {@code - * scrollBounds}, moving through the scrolling range of content, above and below the current - * visible area. The rectangle's vertical position will not account for any scrolling movement - * since capture started. Implementations therefore must track any scroll position changes and - * subtract this distance from requests. + * The requested rectangle describes an area inside the target view, relative to + * <code>scrollBounds</code>. The content may be offscreen, above or below the current visible + * portion of the target view. To handle the request, render the available portion of this + * rectangle to a buffer and return it via the Surface available from {@link + * ScrollCaptureSession#getSurface()}. * <p> - * To handle a request, the content should be scrolled to maximize the visible area of the - * requested rectangle. Offset {@code captureArea} again to account for any further scrolling. + * Note: Implementations are only required to render the requested content, and may do so into + * off-screen buffers without scrolling if they are able. * <p> - * Finally, clip this rectangle against scrollBounds to determine what portion, if any is - * visible content to capture. If the rectangle is completely clipped, set it to {@link - * Rect#setEmpty() empty} and skip the next step. - * <p> - * Make a copy of {@code captureArea}, transform to window coordinates and draw the window, - * clipped to this rectangle, into the {@link ScrollCaptureSession#getSurface() surface} at - * offset (0,0). - * <p> - * Finally, return the resulting {@code captureArea} using - * {@link ScrollCaptureSession#notifyBufferSent}. - * <p> - * If the response is not supplied within XXXms, the session will end with a call to {@link - * #onScrollCaptureEnd}, after which {@code session} is invalid and should be discarded. - * <p> - * TODO: finalize timeout + * The resulting available portion of the request must be computed as a portion of {@code + * captureArea}, and sent to signal the operation is complete, using {@link Consumer#accept + * onComplete.accept}. If the requested rectangle is partially or fully out of bounds the + * resulting portion should be returned. If no portion is available (outside of available + * content), then skip sending any buffer and report an empty Rect as result. * <p> + * This request may be cancelled via the provided {@link CancellationSignal}. When this happens, + * any future call to {@link Consumer#accept onComplete.accept} will be ignored until the next + * request. * + * @param session the current session, resources provided by it are valid for use until the + * {@link #onScrollCaptureEnd(Runnable) session ends} + * @param signal signal to cancel the operation in progress * @param captureArea the area to capture, a rectangle within {@code scrollBounds} + * @param onComplete a consumer for the captured area */ - void onScrollCaptureImageRequest( - @NonNull ScrollCaptureSession session, @NonNull Rect captureArea); + void onScrollCaptureImageRequest(@NonNull ScrollCaptureSession session, + @NonNull CancellationSignal signal, @NonNull Rect captureArea, + @NonNull Consumer<Rect> onComplete); /** * Signals that capture has ended. Implementations should release any temporary resources or * references to objects in use during the capture. Any resources obtained from the session are * now invalid and attempts to use them after this point may throw an exception. * <p> - * The window should be returned as much as possible to its original state when capture started. - * At a minimum, the content should be scrolled to its original position. - * <p> - * <code>onReady</code> should be called when the window should be made visible and - * interactive. The system will wait up to XXXms for this call before proceeding. + * The window should be returned to its original state when capture started. At a minimum, the + * content should be scrolled to its original position. * <p> - * TODO: finalize timeout + * {@link Runnable#run onReady.run} should be called as soon as possible after the window is + * ready for normal interactive use. After the callback (or after a timeout, if not called) the + * screenshot tool will be dismissed and the window may become visible to the user at any time. * * @param onReady a callback to inform the system that the application has completed any * cleanup and is ready to become visible diff --git a/core/java/android/view/ScrollCaptureConnection.java b/core/java/android/view/ScrollCaptureConnection.java index 0e6cdd1dbec5..3456e016c42c 100644 --- a/core/java/android/view/ScrollCaptureConnection.java +++ b/core/java/android/view/ScrollCaptureConnection.java @@ -18,18 +18,23 @@ package android.view; import static java.util.Objects.requireNonNull; +import android.annotation.BinderThread; import android.annotation.NonNull; import android.annotation.UiThread; -import android.annotation.WorkerThread; import android.graphics.Point; import android.graphics.Rect; -import android.os.Handler; +import android.os.CancellationSignal; +import android.os.ICancellationSignal; import android.os.RemoteException; +import android.os.Trace; import android.util.CloseGuard; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; -import java.util.concurrent.atomic.AtomicBoolean; +import java.lang.ref.WeakReference; +import java.util.concurrent.Executor; +import java.util.function.Consumer; /** * Mediator between a selected scroll capture target view and a remote process. @@ -41,270 +46,276 @@ import java.util.concurrent.atomic.AtomicBoolean; public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { private static final String TAG = "ScrollCaptureConnection"; - private static final int DEFAULT_TIMEOUT = 1000; - - private final Handler mHandler; - private ScrollCaptureTarget mSelectedTarget; - private int mTimeoutMillis = DEFAULT_TIMEOUT; - - protected Surface mSurface; - private IScrollCaptureCallbacks mCallbacks; + private final Object mLock = new Object(); private final Rect mScrollBounds; private final Point mPositionInWindow; private final CloseGuard mCloseGuard; + private final Executor mUiThread; + + private ScrollCaptureCallback mLocal; + private IScrollCaptureCallbacks mRemote; - // The current session instance in use by the callback. private ScrollCaptureSession mSession; - // Helps manage timeout callbacks registered to handler and aids testing. - private DelayedAction mTimeoutAction; + private CancellationSignal mCancellation; + + private volatile boolean mStarted; + private volatile boolean mConnected; /** * Constructs a ScrollCaptureConnection. * * @param selectedTarget the target the client is controlling - * @param callbacks the callbacks to reply to system requests + * @param remote the callbacks to reply to system requests * * @hide */ public ScrollCaptureConnection( + @NonNull Executor uiThread, @NonNull ScrollCaptureTarget selectedTarget, - @NonNull IScrollCaptureCallbacks callbacks) { + @NonNull IScrollCaptureCallbacks remote) { + mUiThread = requireNonNull(uiThread, "<uiThread> must non-null"); requireNonNull(selectedTarget, "<selectedTarget> must non-null"); - requireNonNull(callbacks, "<callbacks> must non-null"); - final Rect scrollBounds = requireNonNull(selectedTarget.getScrollBounds(), + mRemote = requireNonNull(remote, "<callbacks> must non-null"); + mScrollBounds = requireNonNull(Rect.copyOrNull(selectedTarget.getScrollBounds()), "target.getScrollBounds() must be non-null to construct a client"); - mSelectedTarget = selectedTarget; - mHandler = selectedTarget.getContainingView().getHandler(); - mScrollBounds = new Rect(scrollBounds); + mLocal = selectedTarget.getCallback(); mPositionInWindow = new Point(selectedTarget.getPositionInWindow()); - mCallbacks = callbacks; mCloseGuard = new CloseGuard(); mCloseGuard.open("close"); - - selectedTarget.getContainingView().addOnAttachStateChangeListener( - new View.OnAttachStateChangeListener() { - @Override - public void onViewAttachedToWindow(View v) { - - } - - @Override - public void onViewDetachedFromWindow(View v) { - selectedTarget.getContainingView().removeOnAttachStateChangeListener(this); - endCapture(); - } - }); - } - - @VisibleForTesting - public void setTimeoutMillis(int timeoutMillis) { - mTimeoutMillis = timeoutMillis; + mConnected = true; } - @VisibleForTesting - public DelayedAction getTimeoutAction() { - return mTimeoutAction; - } - - private void checkConnected() { - if (mSelectedTarget == null || mCallbacks == null) { - throw new IllegalStateException("This client has been disconnected."); + @BinderThread + @Override + public ICancellationSignal startCapture(Surface surface) throws RemoteException { + checkConnected(); + if (!surface.isValid()) { + throw new RemoteException(new IllegalArgumentException("surface must be valid")); } - } - private void checkStarted() { - if (mSession == null) { - throw new IllegalStateException("Capture session has not been started!"); - } - } + ICancellationSignal cancellation = CancellationSignal.createTransport(); + mCancellation = CancellationSignal.fromTransport(cancellation); + mSession = new ScrollCaptureSession(surface, mScrollBounds, mPositionInWindow); - @WorkerThread // IScrollCaptureConnection - @Override - public void startCapture(Surface surface) throws RemoteException { - checkConnected(); - mSurface = surface; - scheduleTimeout(mTimeoutMillis, this::onStartCaptureTimeout); - mSession = new ScrollCaptureSession(mSurface, mScrollBounds, mPositionInWindow, this); - mHandler.post(() -> mSelectedTarget.getCallback().onScrollCaptureStart(mSession, - this::onStartCaptureCompleted)); + Runnable listener = + SafeCallback.create(mCancellation, mUiThread, this::onStartCaptureCompleted); + // -> UiThread + mUiThread.execute(() -> mLocal.onScrollCaptureStart(mSession, mCancellation, listener)); + return cancellation; } @UiThread private void onStartCaptureCompleted() { - if (cancelTimeout()) { - mHandler.post(() -> { - try { - mCallbacks.onCaptureStarted(); - } catch (RemoteException e) { - doShutdown(); - } - }); + mStarted = true; + try { + mRemote.onCaptureStarted(); + } catch (RemoteException e) { + Log.w(TAG, "Shutting down due to error: ", e); + close(); } } - @UiThread - private void onStartCaptureTimeout() { - endCapture(); - } - @WorkerThread // IScrollCaptureConnection + @BinderThread @Override - public void requestImage(Rect requestRect) { + public ICancellationSignal requestImage(Rect requestRect) throws RemoteException { + Trace.beginSection("requestImage"); checkConnected(); checkStarted(); - scheduleTimeout(mTimeoutMillis, this::onRequestImageTimeout); - // Response is dispatched via ScrollCaptureSession, to onRequestImageCompleted - mHandler.post(() -> mSelectedTarget.getCallback().onScrollCaptureImageRequest( - mSession, new Rect(requestRect))); - } - @UiThread - void onRequestImageCompleted(long frameNumber, Rect capturedArea) { - final Rect finalCapturedArea = new Rect(capturedArea); - if (cancelTimeout()) { - mHandler.post(() -> { - try { - mCallbacks.onCaptureBufferSent(frameNumber, finalCapturedArea); - } catch (RemoteException e) { - doShutdown(); - } - }); - } + ICancellationSignal cancellation = CancellationSignal.createTransport(); + mCancellation = CancellationSignal.fromTransport(cancellation); + + Consumer<Rect> listener = + SafeCallback.create(mCancellation, mUiThread, this::onImageRequestCompleted); + // -> UiThread + mUiThread.execute(() -> mLocal.onScrollCaptureImageRequest( + mSession, mCancellation, new Rect(requestRect), listener)); + Trace.endSection(); + return cancellation; } @UiThread - private void onRequestImageTimeout() { - endCapture(); + void onImageRequestCompleted(Rect capturedArea) { + try { + mRemote.onImageRequestCompleted(0, capturedArea); + } catch (RemoteException e) { + Log.w(TAG, "Shutting down due to error: ", e); + close(); + } } - @WorkerThread // IScrollCaptureConnection + @BinderThread @Override - public void endCapture() { - if (isStarted()) { - scheduleTimeout(mTimeoutMillis, this::onEndCaptureTimeout); - mHandler.post(() -> - mSelectedTarget.getCallback().onScrollCaptureEnd(this::onEndCaptureCompleted)); - } else { - disconnect(); - } - } + public ICancellationSignal endCapture() throws RemoteException { + checkConnected(); + checkStarted(); - private boolean isStarted() { - return mCallbacks != null && mSelectedTarget != null; - } + ICancellationSignal cancellation = CancellationSignal.createTransport(); + mCancellation = CancellationSignal.fromTransport(cancellation); - @UiThread - private void onEndCaptureCompleted() { // onEndCaptureCompleted - if (cancelTimeout()) { - doShutdown(); - } + Runnable listener = + SafeCallback.create(mCancellation, mUiThread, this::onEndCaptureCompleted); + // -> UiThread + mUiThread.execute(() -> mLocal.onScrollCaptureEnd(listener)); + return cancellation; } @UiThread - private void onEndCaptureTimeout() { - doShutdown(); + private void onEndCaptureCompleted() { + synchronized (mLock) { + mStarted = false; + try { + mRemote.onCaptureEnded(); + } catch (RemoteException e) { + Log.w(TAG, "Shutting down due to error: ", e); + close(); + } + } } + @BinderThread + @Override + public void close() { + if (mStarted) { + Log.w(TAG, "close(): capture is still started?! Ending now."); - private void doShutdown() { - try { - if (mCallbacks != null) { - mCallbacks.onConnectionClosed(); - } - } catch (RemoteException e) { - // Ignore - } finally { - disconnect(); + // -> UiThread + mUiThread.execute(() -> mLocal.onScrollCaptureEnd(() -> { /* ignore */ })); + mStarted = false; } + disconnect(); } /** * Shuts down this client and releases references to dependent objects. No attempt is made * to notify the controller, use with caution! */ - public void disconnect() { - if (mSession != null) { - mSession.disconnect(); + private void disconnect() { + synchronized (mLock) { mSession = null; + mConnected = false; + mStarted = false; + mRemote = null; + mLocal = null; + mCloseGuard.close(); } + } + + public boolean isConnected() { + return mConnected; + } - mSelectedTarget = null; - mCallbacks = null; + public boolean isStarted() { + return mStarted; + } + + private synchronized void checkConnected() throws RemoteException { + synchronized (mLock) { + if (!mConnected) { + throw new RemoteException(new IllegalStateException("Not connected")); + } + } + } + + private void checkStarted() throws RemoteException { + synchronized (mLock) { + if (!mStarted) { + throw new RemoteException(new IllegalStateException("Not started!")); + } + } } /** @return a string representation of the state of this client */ public String toString() { return "ScrollCaptureConnection{" + + "connected=" + mConnected + + ", started=" + mStarted + ", session=" + mSession - + ", selectedTarget=" + mSelectedTarget - + ", clientCallbacks=" + mCallbacks + + ", remote=" + mRemote + + ", local=" + mLocal + "}"; } - private boolean cancelTimeout() { - if (mTimeoutAction != null) { - return mTimeoutAction.cancel(); - } - return false; + @VisibleForTesting + public CancellationSignal getCancellation() { + return mCancellation; } - private void scheduleTimeout(long timeoutMillis, Runnable action) { - if (mTimeoutAction != null) { - mTimeoutAction.cancel(); + protected void finalize() throws Throwable { + try { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + close(); + } finally { + super.finalize(); } - mTimeoutAction = new DelayedAction(mHandler, timeoutMillis, action); } - /** @hide */ - @VisibleForTesting - public static class DelayedAction { - private final AtomicBoolean mCompleted = new AtomicBoolean(); - private final Object mToken = new Object(); - private final Handler mHandler; - private final Runnable mAction; - - @VisibleForTesting - public DelayedAction(Handler handler, long timeoutMillis, Runnable action) { - mHandler = handler; - mAction = action; - mHandler.postDelayed(this::onTimeout, mToken, timeoutMillis); + private static class SafeCallback<T> { + private final CancellationSignal mSignal; + private final WeakReference<T> mTargetRef; + private final Executor mExecutor; + private boolean mExecuted; + + protected SafeCallback(CancellationSignal signal, Executor executor, T target) { + mSignal = signal; + mTargetRef = new WeakReference<>(target); + mExecutor = executor; } - private boolean onTimeout() { - if (mCompleted.compareAndSet(false, true)) { - mAction.run(); - return true; + // Provide the target to the consumer to invoke, forward on handler thread ONCE, + // and only if noy cancelled, and the target is still available (not collected) + protected final void maybeAccept(Consumer<T> targetConsumer) { + if (mExecuted) { + return; + } + mExecuted = true; + if (mSignal.isCanceled()) { + return; + } + T target = mTargetRef.get(); + if (target == null) { + return; } - return false; + mExecutor.execute(() -> targetConsumer.accept(target)); } - /** - * Cause the timeout action to run immediately and mark as timed out. - * - * @return true if the timeout was run, false if the timeout had already been canceled - */ - @VisibleForTesting - public boolean timeoutNow() { - return onTimeout(); + static Runnable create(CancellationSignal signal, Executor executor, Runnable target) { + return new RunnableCallback(signal, executor, target); } - /** - * Attempt to cancel the timeout action (such as after a callback is made) - * - * @return true if the timeout was canceled and will not run, false if time has expired and - * the timeout action has or will run momentarily - */ - public boolean cancel() { - if (!mCompleted.compareAndSet(false, true)) { - // Whoops, too late! - return false; - } - mHandler.removeCallbacksAndMessages(mToken); - return true; + static <T> Consumer<T> create(CancellationSignal signal, Executor executor, + Consumer<T> target) { + return new ConsumerCallback<T>(signal, executor, target); + } + } + + private static final class RunnableCallback extends SafeCallback<Runnable> implements Runnable { + RunnableCallback(CancellationSignal signal, Executor executor, Runnable target) { + super(signal, executor, target); + } + + @Override + public void run() { + maybeAccept(Runnable::run); + } + } + + private static final class ConsumerCallback<T> extends SafeCallback<Consumer<T>> + implements Consumer<T> { + ConsumerCallback(CancellationSignal signal, Executor executor, Consumer<T> target) { + super(signal, executor, target); + } + + @Override + public void accept(T value) { + maybeAccept((target) -> target.accept(value)); } } } diff --git a/core/java/android/view/ScrollCaptureResponse.aidl b/core/java/android/view/ScrollCaptureResponse.aidl new file mode 100644 index 000000000000..3de2b80ef16e --- /dev/null +++ b/core/java/android/view/ScrollCaptureResponse.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +parcelable ScrollCaptureResponse; diff --git a/core/java/android/view/ScrollCaptureResponse.java b/core/java/android/view/ScrollCaptureResponse.java new file mode 100644 index 000000000000..564113edb3c7 --- /dev/null +++ b/core/java/android/view/ScrollCaptureResponse.java @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.Rect; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +import java.util.ArrayList; + +/** @hide */ +@DataClass(genToString = true, genGetters = true) +public class ScrollCaptureResponse implements Parcelable { + + /** Developer-facing human readable description of the result. */ + @NonNull + private String mDescription = ""; + + // Remaining fields are non-null when isConnected() == true + + /** The active connection for a successful result. */ + @Nullable + @DataClass.MaySetToNull + private IScrollCaptureConnection mConnection = null; + + /** The bounds of the window within the display */ + @Nullable + private Rect mWindowBounds = null; + + /** The bounds of the scrolling content, in window space. */ + @Nullable + private Rect mBoundsInWindow = null; + + /** The current window title. */ + @Nullable + private String mWindowTitle = null; + + /** Carries additional logging and debugging information when enabled. */ + @NonNull + @DataClass.PluralOf("message") + private ArrayList<String> mMessages = new ArrayList<>(); + + /** Whether a connection has been returned. */ + public boolean isConnected() { + return mConnection != null; + } + + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/ScrollCaptureResponse.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + /* package-private */ ScrollCaptureResponse( + @NonNull String description, + @Nullable IScrollCaptureConnection connection, + @Nullable Rect windowBounds, + @Nullable Rect boundsInWindow, + @Nullable String windowTitle, + @NonNull ArrayList<String> messages) { + this.mDescription = description; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mDescription); + this.mConnection = connection; + this.mWindowBounds = windowBounds; + this.mBoundsInWindow = boundsInWindow; + this.mWindowTitle = windowTitle; + this.mMessages = messages; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mMessages); + + // onConstructed(); // You can define this method to get a callback + } + + /** + * Developer-facing human readable description of the result. + */ + @DataClass.Generated.Member + public @NonNull String getDescription() { + return mDescription; + } + + /** + * The active connection for a successful result. + */ + @DataClass.Generated.Member + public @Nullable IScrollCaptureConnection getConnection() { + return mConnection; + } + + /** + * The bounds of the window within the display + */ + @DataClass.Generated.Member + public @Nullable Rect getWindowBounds() { + return mWindowBounds; + } + + /** + * The bounds of the scrolling content, in window space. + */ + @DataClass.Generated.Member + public @Nullable Rect getBoundsInWindow() { + return mBoundsInWindow; + } + + /** + * The current window title. + */ + @DataClass.Generated.Member + public @Nullable String getWindowTitle() { + return mWindowTitle; + } + + /** + * Carries additional logging and debugging information when enabled. + */ + @DataClass.Generated.Member + public @NonNull ArrayList<String> getMessages() { + return mMessages; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "ScrollCaptureResponse { " + + "description = " + mDescription + ", " + + "connection = " + mConnection + ", " + + "windowBounds = " + mWindowBounds + ", " + + "boundsInWindow = " + mBoundsInWindow + ", " + + "windowTitle = " + mWindowTitle + ", " + + "messages = " + mMessages + + " }"; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mConnection != null) flg |= 0x2; + if (mWindowBounds != null) flg |= 0x4; + if (mBoundsInWindow != null) flg |= 0x8; + if (mWindowTitle != null) flg |= 0x10; + dest.writeByte(flg); + dest.writeString(mDescription); + if (mConnection != null) dest.writeStrongInterface(mConnection); + if (mWindowBounds != null) dest.writeTypedObject(mWindowBounds, flags); + if (mBoundsInWindow != null) dest.writeTypedObject(mBoundsInWindow, flags); + if (mWindowTitle != null) dest.writeString(mWindowTitle); + dest.writeStringList(mMessages); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + protected ScrollCaptureResponse(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + String description = in.readString(); + IScrollCaptureConnection connection = (flg & 0x2) == 0 ? null : IScrollCaptureConnection.Stub.asInterface(in.readStrongBinder()); + Rect windowBounds = (flg & 0x4) == 0 ? null : (Rect) in.readTypedObject(Rect.CREATOR); + Rect boundsInWindow = (flg & 0x8) == 0 ? null : (Rect) in.readTypedObject(Rect.CREATOR); + String windowTitle = (flg & 0x10) == 0 ? null : in.readString(); + ArrayList<String> messages = new ArrayList<>(); + in.readStringList(messages); + + this.mDescription = description; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mDescription); + this.mConnection = connection; + this.mWindowBounds = windowBounds; + this.mBoundsInWindow = boundsInWindow; + this.mWindowTitle = windowTitle; + this.mMessages = messages; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mMessages); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<ScrollCaptureResponse> CREATOR + = new Parcelable.Creator<ScrollCaptureResponse>() { + @Override + public ScrollCaptureResponse[] newArray(int size) { + return new ScrollCaptureResponse[size]; + } + + @Override + public ScrollCaptureResponse createFromParcel(@NonNull android.os.Parcel in) { + return new ScrollCaptureResponse(in); + } + }; + + /** + * A builder for {@link ScrollCaptureResponse} + */ + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static class Builder { + + private @NonNull String mDescription; + private @Nullable IScrollCaptureConnection mConnection; + private @Nullable Rect mWindowBounds; + private @Nullable Rect mBoundsInWindow; + private @Nullable String mWindowTitle; + private @NonNull ArrayList<String> mMessages; + + private long mBuilderFieldsSet = 0L; + + public Builder() { + } + + /** + * Developer-facing human readable description of the result. + */ + @DataClass.Generated.Member + public @NonNull Builder setDescription(@NonNull String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1; + mDescription = value; + return this; + } + + /** + * The active connection for a successful result. + */ + @DataClass.Generated.Member + public @NonNull Builder setConnection(@Nullable IScrollCaptureConnection value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mConnection = value; + return this; + } + + /** + * The bounds of the window within the display + */ + @DataClass.Generated.Member + public @NonNull Builder setWindowBounds(@NonNull Rect value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; + mWindowBounds = value; + return this; + } + + /** + * The bounds of the scrolling content, in window space. + */ + @DataClass.Generated.Member + public @NonNull Builder setBoundsInWindow(@NonNull Rect value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x8; + mBoundsInWindow = value; + return this; + } + + /** + * The current window title. + */ + @DataClass.Generated.Member + public @NonNull Builder setWindowTitle(@NonNull String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x10; + mWindowTitle = value; + return this; + } + + /** + * Carries additional logging and debugging information when enabled. + */ + @DataClass.Generated.Member + public @NonNull Builder setMessages(@NonNull ArrayList<String> value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x20; + mMessages = value; + return this; + } + + /** @see #setMessages */ + @DataClass.Generated.Member + public @NonNull Builder addMessage(@NonNull String value) { + if (mMessages == null) setMessages(new ArrayList<>()); + mMessages.add(value); + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @NonNull ScrollCaptureResponse build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x40; // Mark builder used + + if ((mBuilderFieldsSet & 0x1) == 0) { + mDescription = ""; + } + if ((mBuilderFieldsSet & 0x2) == 0) { + mConnection = null; + } + if ((mBuilderFieldsSet & 0x4) == 0) { + mWindowBounds = null; + } + if ((mBuilderFieldsSet & 0x8) == 0) { + mBoundsInWindow = null; + } + if ((mBuilderFieldsSet & 0x10) == 0) { + mWindowTitle = null; + } + if ((mBuilderFieldsSet & 0x20) == 0) { + mMessages = new ArrayList<>(); + } + ScrollCaptureResponse o = new ScrollCaptureResponse( + mDescription, + mConnection, + mWindowBounds, + mBoundsInWindow, + mWindowTitle, + mMessages); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x40) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } + } + + @DataClass.Generated( + time = 1612282689462L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/view/ScrollCaptureResponse.java", + inputSignatures = "private @android.annotation.NonNull java.lang.String mDescription\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.MaySetToNull android.view.IScrollCaptureConnection mConnection\nprivate @android.annotation.Nullable android.graphics.Rect mWindowBounds\nprivate @android.annotation.Nullable android.graphics.Rect mBoundsInWindow\nprivate @android.annotation.Nullable java.lang.String mWindowTitle\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"message\") java.util.ArrayList<java.lang.String> mMessages\npublic boolean isConnected()\nclass ScrollCaptureResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genGetters=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/view/ScrollCaptureSearchResults.java b/core/java/android/view/ScrollCaptureSearchResults.java new file mode 100644 index 000000000000..3469b9dc7103 --- /dev/null +++ b/core/java/android/view/ScrollCaptureSearchResults.java @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import static java.util.Objects.requireNonNull; + +import android.annotation.NonNull; +import android.annotation.UiThread; +import android.graphics.Rect; +import android.os.CancellationSignal; +import android.util.IndentingPrintWriter; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +/** + * Collects nodes in the view hierarchy which have been identified as scrollable content. + * + * @hide + */ +@UiThread +public final class ScrollCaptureSearchResults { + private final Executor mExecutor; + private final List<ScrollCaptureTarget> mTargets; + private final CancellationSignal mCancel; + + private Runnable mOnCompleteListener; + private int mCompleted; + private boolean mComplete = true; + + public ScrollCaptureSearchResults(Executor executor) { + mExecutor = executor; + mTargets = new ArrayList<>(); + mCancel = new CancellationSignal(); + } + + // Public + + /** + * Add the given target to the results. + * + * @param target the target to consider + */ + public void addTarget(@NonNull ScrollCaptureTarget target) { + requireNonNull(target); + + mTargets.add(target); + mComplete = false; + final ScrollCaptureCallback callback = target.getCallback(); + final Consumer<Rect> consumer = new SearchRequest(target); + + // Defer so the view hierarchy scan completes first + mExecutor.execute( + () -> callback.onScrollCaptureSearch(mCancel, consumer)); + } + + public boolean isComplete() { + return mComplete; + } + + /** + * Provides a callback to be invoked as soon as all responses have been received from all + * targets to this point. + * + * @param onComplete listener to add + */ + public void setOnCompleteListener(Runnable onComplete) { + if (mComplete) { + onComplete.run(); + } else { + mOnCompleteListener = onComplete; + } + } + + /** + * Indicates whether the search results are empty. + * + * @return true if no targets have been added + */ + public boolean isEmpty() { + return mTargets.isEmpty(); + } + + /** + * Force the results to complete now, cancelling any pending requests and calling a complete + * listener if provided. + */ + public void finish() { + if (!mComplete) { + mCancel.cancel(); + signalComplete(); + } + } + + private void signalComplete() { + mComplete = true; + mTargets.sort(PRIORITY_ORDER); + if (mOnCompleteListener != null) { + mOnCompleteListener.run(); + mOnCompleteListener = null; + } + } + + @VisibleForTesting + public List<ScrollCaptureTarget> getTargets() { + return new ArrayList<>(mTargets); + } + + /** + * Get the top ranked result out of all completed requests. + * + * @return the top ranked result + */ + public ScrollCaptureTarget getTopResult() { + ScrollCaptureTarget target = mTargets.isEmpty() ? null : mTargets.get(0); + return target != null && target.getScrollBounds() != null ? target : null; + } + + private class SearchRequest implements Consumer<Rect> { + private ScrollCaptureTarget mTarget; + + SearchRequest(ScrollCaptureTarget target) { + mTarget = target; + } + + @Override + public void accept(Rect scrollBounds) { + if (mTarget == null || mCancel.isCanceled()) { + return; + } + mExecutor.execute(() -> consume(scrollBounds)); + } + + private void consume(Rect scrollBounds) { + if (mTarget == null || mCancel.isCanceled()) { + return; + } + if (!nullOrEmpty(scrollBounds)) { + mTarget.setScrollBounds(scrollBounds); + mTarget.updatePositionInWindow(); + } + mCompleted++; + mTarget = null; + + // All done? + if (mCompleted == mTargets.size()) { + signalComplete(); + } + } + } + + private static final int AFTER = 1; + private static final int BEFORE = -1; + private static final int EQUAL = 0; + + static final Comparator<ScrollCaptureTarget> PRIORITY_ORDER = (a, b) -> { + if (a == null && b == null) { + return 0; + } else if (a == null || b == null) { + return (a == null) ? 1 : -1; + } + + boolean emptyScrollBoundsA = nullOrEmpty(a.getScrollBounds()); + boolean emptyScrollBoundsB = nullOrEmpty(b.getScrollBounds()); + if (emptyScrollBoundsA || emptyScrollBoundsB) { + if (emptyScrollBoundsA && emptyScrollBoundsB) { + return EQUAL; + } + // Prefer the one with a non-empty scroll bounds + if (emptyScrollBoundsA) { + return AFTER; + } + return BEFORE; + } + + final View viewA = a.getContainingView(); + final View viewB = b.getContainingView(); + + // Prefer any view with scrollCaptureHint="INCLUDE", over one without + // This is an escape hatch for the next rule (descendants first) + boolean hintIncludeA = hasIncludeHint(viewA); + boolean hintIncludeB = hasIncludeHint(viewB); + if (hintIncludeA != hintIncludeB) { + return (hintIncludeA) ? BEFORE : AFTER; + } + // If the views are relatives, prefer the descendant. This allows implementations to + // leverage nested scrolling APIs by interacting with the innermost scrollable view (as + // would happen with touch input). + if (isDescendant(viewA, viewB)) { + return BEFORE; + } + if (isDescendant(viewB, viewA)) { + return AFTER; + } + + // finally, prefer one with larger scroll bounds + int scrollAreaA = area(a.getScrollBounds()); + int scrollAreaB = area(b.getScrollBounds()); + return (scrollAreaA >= scrollAreaB) ? BEFORE : AFTER; + }; + + private static int area(Rect r) { + return r.width() * r.height(); + } + + private static boolean nullOrEmpty(Rect r) { + return r == null || r.isEmpty(); + } + + private static boolean hasIncludeHint(View view) { + return (view.getScrollCaptureHint() & View.SCROLL_CAPTURE_HINT_INCLUDE) != 0; + } + + /** + * Determines if {@code otherView} is a descendant of {@code view}. + * + * @param view a view + * @param otherView another view + * @return true if {@code view} is an ancestor of {@code otherView} + */ + private static boolean isDescendant(@NonNull View view, @NonNull View otherView) { + if (view == otherView) { + return false; + } + ViewParent otherParent = otherView.getParent(); + while (otherParent != view && otherParent != null) { + otherParent = otherParent.getParent(); + } + return otherParent == view; + } + + void dump(IndentingPrintWriter writer) { + writer.println("results:"); + writer.increaseIndent(); + writer.println("complete: " + isComplete()); + writer.println("cancelled: " + mCancel.isCanceled()); + writer.println("targets:"); + writer.increaseIndent(); + if (isEmpty()) { + writer.println("None"); + } else { + for (int i = 0; i < mTargets.size(); i++) { + writer.println("[" + i + "]"); + writer.increaseIndent(); + mTargets.get(i).dump(writer); + writer.decreaseIndent(); + } + writer.decreaseIndent(); + } + writer.decreaseIndent(); + } +} diff --git a/core/java/android/view/ScrollCaptureSession.java b/core/java/android/view/ScrollCaptureSession.java index 92617a325265..748e7ea56f41 100644 --- a/core/java/android/view/ScrollCaptureSession.java +++ b/core/java/android/view/ScrollCaptureSession.java @@ -16,18 +16,15 @@ package android.view; +import static java.util.Objects.requireNonNull; + import android.annotation.NonNull; -import android.annotation.Nullable; import android.graphics.Point; import android.graphics.Rect; /** * A session represents the scope of interaction between a {@link ScrollCaptureCallback} and the - * system during an active scroll capture operation. During the scope of a session, a callback - * will receive a series of requests for image data. Resources provided here are valid for use - * until {@link ScrollCaptureCallback#onScrollCaptureEnd(Runnable)}. - * - * @hide + * system during an active scroll capture operation. */ public class ScrollCaptureSession { @@ -35,22 +32,27 @@ public class ScrollCaptureSession { private final Rect mScrollBounds; private final Point mPositionInWindow; - @Nullable - private ScrollCaptureConnection mConnection; - - /** @hide */ - public ScrollCaptureSession(Surface surface, Rect scrollBounds, Point positionInWindow, - ScrollCaptureConnection connection) { - mSurface = surface; - mScrollBounds = scrollBounds; - mPositionInWindow = positionInWindow; - mConnection = connection; + /** + * Constructs a new session instance. + * + * @param surface the surface to consume generated images + * @param scrollBounds the bounds of the capture area within the containing view + * @param positionInWindow the offset of scrollBounds within the window + */ + public ScrollCaptureSession(@NonNull Surface surface, @NonNull Rect scrollBounds, + @NonNull Point positionInWindow) { + mSurface = requireNonNull(surface); + mScrollBounds = requireNonNull(scrollBounds); + mPositionInWindow = requireNonNull(positionInWindow); } /** * Returns a * <a href="https://source.android.com/devices/graphics/arch-bq-gralloc">BufferQueue</a> in the * form of a {@link Surface} for transfer of image buffers. + * <p> + * The surface is guaranteed to remain {@link Surface#isValid() valid} until the session + * {@link ScrollCaptureCallback#onScrollCaptureEnd(Runnable) ends}. * * @return the surface for transferring image buffers * @throws IllegalStateException if the session has been closed @@ -80,26 +82,4 @@ public class ScrollCaptureSession { public Point getPositionInWindow() { return mPositionInWindow; } - - /** - * Notify the system that an a buffer has been posted via the getSurface() channel. - * - * @param frameNumber the frame number of the queued buffer - * @param capturedArea the area captured, relative to scroll bounds - */ - public void notifyBufferSent(long frameNumber, @NonNull Rect capturedArea) { - if (mConnection != null) { - mConnection.onRequestImageCompleted(frameNumber, capturedArea); - } - } - - /** - * @hide - */ - public void disconnect() { - mConnection = null; - if (mSurface.isValid()) { - mSurface.release(); - } - } } diff --git a/core/java/android/view/ScrollCaptureTarget.java b/core/java/android/view/ScrollCaptureTarget.java index f3fcabb26b31..4fd48892da70 100644 --- a/core/java/android/view/ScrollCaptureTarget.java +++ b/core/java/android/view/ScrollCaptureTarget.java @@ -22,14 +22,16 @@ import android.annotation.UiThread; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; +import android.os.CancellationSignal; import com.android.internal.util.FastMath; +import java.io.PrintWriter; +import java.util.function.Consumer; + /** * A target collects the set of contextual information for a ScrollCaptureHandler discovered during * a {@link View#dispatchScrollCaptureSearch scroll capture search}. - * - * @hide */ public final class ScrollCaptureTarget { private final View mContainingView; @@ -41,7 +43,6 @@ public final class ScrollCaptureTarget { private final float[] mTmpFloatArr = new float[2]; private final Matrix mMatrixViewLocalToWindow = new Matrix(); - private final Rect mTmpRect = new Rect(); public ScrollCaptureTarget(@NonNull View scrollTarget, @NonNull Rect localVisibleRect, @NonNull Point positionInWindow, @NonNull ScrollCaptureCallback callback) { @@ -52,7 +53,10 @@ public final class ScrollCaptureTarget { mPositionInWindow = positionInWindow; } - /** @return the hint that the {@code containing view} had during the scroll capture search */ + /** + * @return the hint that the {@code containing view} had during the scroll capture search + * @see View#getScrollCaptureHint() + */ @View.ScrollCaptureHint public int getHint() { return mHint; @@ -71,8 +75,7 @@ public final class ScrollCaptureTarget { } /** - * Returns the un-clipped, visible bounds of the containing view during the scroll capture - * search. This is used to determine on-screen area to assist in selecting the primary target. + * Returns the visible bounds of the containing view. * * @return the visible bounds of the {@code containing view} in view-local coordinates */ @@ -81,13 +84,17 @@ public final class ScrollCaptureTarget { return mLocalVisibleRect; } - /** @return the position of the {@code containing view} within the window */ + /** @return the position of the visible bounds of the containing view within the window */ @NonNull public Point getPositionInWindow() { return mPositionInWindow; } - /** @return the {@code scroll bounds} for this {@link ScrollCaptureCallback callback} */ + /** + * @return the {@code scroll bounds} for this {@link ScrollCaptureCallback callback} + * + * @see ScrollCaptureCallback#onScrollCaptureSearch(CancellationSignal, Consumer) + */ @Nullable public Rect getScrollBounds() { return mScrollBounds; @@ -119,8 +126,8 @@ public final class ScrollCaptureTarget { } /** - * Refresh the value of {@link #mLocalVisibleRect} and {@link #mPositionInWindow} based on the - * current state of the {@code containing view}. + * Refresh the local visible bounds and it's offset within the window, based on the current + * state of the {@code containing view}. */ @UiThread public void updatePositionInWindow() { @@ -132,4 +139,27 @@ public final class ScrollCaptureTarget { roundIntoPoint(mPositionInWindow, mTmpFloatArr); } + public String toString() { + return "ScrollCaptureTarget{" + "view=" + mContainingView + + ", callback=" + mCallback + + ", scrollBounds=" + mScrollBounds + + ", localVisibleRect=" + mLocalVisibleRect + + ", positionInWindow=" + mPositionInWindow + + "}"; + } + + void dump(@NonNull PrintWriter writer) { + View view = getContainingView(); + writer.println("view: " + view); + writer.println("hint: " + mHint); + writer.println("callback: " + mCallback); + writer.println("scrollBounds: " + + (mScrollBounds == null ? "null" : mScrollBounds.toShortString())); + Point inWindow = getPositionInWindow(); + writer.println("positionInWindow: " + + ((inWindow == null) ? "null" : "[" + inWindow.x + "," + inWindow.y + "]")); + Rect localVisible = getLocalVisibleRect(); + writer.println("localVisibleRect: " + + (localVisible == null ? "null" : localVisible.toShortString())); + } } diff --git a/core/java/android/view/ScrollCaptureTargetResolver.java b/core/java/android/view/ScrollCaptureTargetResolver.java deleted file mode 100644 index e4316bbc9397..000000000000 --- a/core/java/android/view/ScrollCaptureTargetResolver.java +++ /dev/null @@ -1,337 +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 android.view; - -import android.annotation.AnyThread; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.UiThread; -import android.graphics.Rect; -import android.os.Handler; -import android.os.Looper; -import android.os.SystemClock; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; - - -import java.util.Queue; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; - -/** - * Queries additional state from a list of {@link ScrollCaptureTarget targets} via asynchronous - * callbacks, then aggregates and reduces the target list to a single target, or null if no target - * is suitable. - * <p> - * The rules for selection are (in order): - * <ul> - * <li>prefer getScrollBounds(): non-empty - * <li>prefer View.getScrollCaptureHint == SCROLL_CAPTURE_HINT_INCLUDE - * <li>prefer descendants before parents - * <li>prefer larger area for getScrollBounds() (clipped to view bounds) - * </ul> - * - * <p> - * All calls to {@link ScrollCaptureCallback#onScrollCaptureSearch} are made on the main thread, - * with results are queued and consumed to the main thread as well. - * - * @see #start(Handler, long, Consumer) - * - * @hide - */ -@UiThread -public class ScrollCaptureTargetResolver { - private static final String TAG = "ScrollCaptureTargetRes"; - - private final Object mLock = new Object(); - - private final Queue<ScrollCaptureTarget> mTargets; - private Handler mHandler; - private long mTimeLimitMillis; - - private Consumer<ScrollCaptureTarget> mWhenComplete; - private int mPendingBoundsRequests; - private long mDeadlineMillis; - - private ScrollCaptureTarget mResult; - private boolean mFinished; - - private boolean mStarted; - - private static int area(Rect r) { - return r.width() * r.height(); - } - - private static boolean nullOrEmpty(Rect r) { - return r == null || r.isEmpty(); - } - - /** - * Binary operator which selects the best {@link ScrollCaptureTarget}. - */ - private static ScrollCaptureTarget chooseTarget(ScrollCaptureTarget a, ScrollCaptureTarget b) { - if (a == null && b == null) { - return null; - } else if (a == null || b == null) { - ScrollCaptureTarget c = (a == null) ? b : a; - return c; - } - - boolean emptyScrollBoundsA = nullOrEmpty(a.getScrollBounds()); - boolean emptyScrollBoundsB = nullOrEmpty(b.getScrollBounds()); - if (emptyScrollBoundsA || emptyScrollBoundsB) { - if (emptyScrollBoundsA && emptyScrollBoundsB) { - // Both have an empty or null scrollBounds - Log.d(TAG, "chooseTarget: (both have empty or null bounds) return " + null); - return null; - } - // Prefer the one with a non-empty scroll bounds - if (emptyScrollBoundsA) { - Log.d(TAG, "chooseTarget: (a has empty or null bounds) return " + b); - return b; - } - Log.d(TAG, "chooseTarget: (b has empty or null bounds) return " + a); - return a; - } - - final View viewA = a.getContainingView(); - final View viewB = b.getContainingView(); - - // Prefer any view with scrollCaptureHint="INCLUDE", over one without - // This is an escape hatch for the next rule (descendants first) - boolean hintIncludeA = hasIncludeHint(viewA); - boolean hintIncludeB = hasIncludeHint(viewB); - if (hintIncludeA != hintIncludeB) { - ScrollCaptureTarget c = (hintIncludeA) ? a : b; - Log.d(TAG, "chooseTarget: (has hint=INCLUDE) return " + c); - return c; - } - - // If the views are relatives, prefer the descendant. This allows implementations to - // leverage nested scrolling APIs by interacting with the innermost scrollable view (as - // would happen with touch input). - if (isDescendant(viewA, viewB)) { - Log.d(TAG, "chooseTarget: (b is descendant of a) return " + b); - return b; - } - if (isDescendant(viewB, viewA)) { - Log.d(TAG, "chooseTarget: (a is descendant of b) return " + a); - return a; - } - - // finally, prefer one with larger scroll bounds - int scrollAreaA = area(a.getScrollBounds()); - int scrollAreaB = area(b.getScrollBounds()); - ScrollCaptureTarget c = (scrollAreaA >= scrollAreaB) ? a : b; - Log.d(TAG, "chooseTarget: return " + c); - return c; - } - - /** - * Creates an instance to query and filter {@code target}. - * - * @param targets a list of {@link ScrollCaptureTarget} as collected by {@link - * View#dispatchScrollCaptureSearch}. - * @see #start(Handler, long, Consumer) - */ - public ScrollCaptureTargetResolver(Queue<ScrollCaptureTarget> targets) { - mTargets = targets; - } - - void checkThread() { - if (mHandler.getLooper() != Looper.myLooper()) { - throw new IllegalStateException("Called from wrong thread! (" - + Thread.currentThread().getName() + ")"); - } - } - - /** - * Blocks until a result is returned (after completion or timeout). - * <p> - * For testing only. Normal usage should receive a callback after calling {@link #start}. - */ - @VisibleForTesting - public ScrollCaptureTarget waitForResult() throws InterruptedException { - synchronized (mLock) { - while (!mFinished) { - mLock.wait(); - } - } - return mResult; - } - - private void supplyResult(ScrollCaptureTarget target) { - checkThread(); - if (mFinished) { - return; - } - mResult = chooseTarget(mResult, target); - boolean finish = mPendingBoundsRequests == 0 - || SystemClock.uptimeMillis() >= mDeadlineMillis; - if (finish) { - mPendingBoundsRequests = 0; - mWhenComplete.accept(mResult); - synchronized (mLock) { - mFinished = true; - mLock.notify(); - } - mWhenComplete = null; - } - } - - /** - * Asks all targets for {@link ScrollCaptureCallback#onScrollCaptureSearch(Consumer) - * scrollBounds}, and selects the primary target according to the {@link - * #chooseTarget} function. - * - * @param timeLimitMillis the amount of time to wait for all responses before delivering the top - * result - * @param resultConsumer the consumer to receive the primary target - */ - @AnyThread - public void start(Handler uiHandler, long timeLimitMillis, - Consumer<ScrollCaptureTarget> resultConsumer) { - synchronized (mLock) { - if (mStarted) { - throw new IllegalStateException("already started!"); - } - if (timeLimitMillis < 0) { - throw new IllegalArgumentException("Time limit must be positive"); - } - mHandler = uiHandler; - mTimeLimitMillis = timeLimitMillis; - mWhenComplete = resultConsumer; - if (mTargets.isEmpty()) { - mHandler.post(() -> supplyResult(null)); - return; - } - mStarted = true; - uiHandler.post(this::run); - } - } - - private void run() { - checkThread(); - - mPendingBoundsRequests = mTargets.size(); - for (ScrollCaptureTarget target : mTargets) { - queryTarget(target); - } - mDeadlineMillis = SystemClock.uptimeMillis() + mTimeLimitMillis; - mHandler.postAtTime(mTimeoutRunnable, mDeadlineMillis); - } - - private final Runnable mTimeoutRunnable = () -> { - checkThread(); - supplyResult(null); - }; - - /** - * Adds a target to the list and requests {@link ScrollCaptureCallback#onScrollCaptureSearch} - * scrollBounds} from it. Results are returned by a call to {@link #onScrollBoundsProvided}. - * - * @param target the target to add - */ - @UiThread - private void queryTarget(@NonNull ScrollCaptureTarget target) { - checkThread(); - final ScrollCaptureCallback callback = target.getCallback(); - // from the UI thread, request scroll bounds - callback.onScrollCaptureSearch( - // allow only one callback to onReady.accept(): - new SingletonConsumer<Rect>( - // Queue and consume on the UI thread - ((scrollBounds) -> mHandler.post( - () -> onScrollBoundsProvided(target, scrollBounds))))); - } - - @UiThread - private void onScrollBoundsProvided(ScrollCaptureTarget target, @Nullable Rect scrollBounds) { - checkThread(); - if (mFinished) { - return; - } - - // Record progress. - mPendingBoundsRequests--; - - // Remove the timeout. - mHandler.removeCallbacks(mTimeoutRunnable); - - boolean doneOrTimedOut = mPendingBoundsRequests == 0 - || SystemClock.uptimeMillis() >= mDeadlineMillis; - - final View containingView = target.getContainingView(); - if (!nullOrEmpty(scrollBounds) && containingView.isAggregatedVisible()) { - target.updatePositionInWindow(); - target.setScrollBounds(scrollBounds); - supplyResult(target); - } - - if (!mFinished) { - // Reschedule the timeout. - mHandler.postAtTime(mTimeoutRunnable, mDeadlineMillis); - } - } - - private static boolean hasIncludeHint(View view) { - return (view.getScrollCaptureHint() & View.SCROLL_CAPTURE_HINT_INCLUDE) != 0; - } - - /** - * Determines if {@code otherView} is a descendant of {@code view}. - * - * @param view a view - * @param otherView another view - * @return true if {@code view} is an ancestor of {@code otherView} - */ - private static boolean isDescendant(@NonNull View view, @NonNull View otherView) { - if (view == otherView) { - return false; - } - ViewParent otherParent = otherView.getParent(); - while (otherParent != view && otherParent != null) { - otherParent = otherParent.getParent(); - } - return otherParent == view; - } - - /** - * A safe wrapper for a consumer callbacks intended to accept a single value. It ensures - * that the receiver of the consumer does not retain a reference to {@code target} after use nor - * cause race conditions by invoking {@link Consumer#accept accept} more than once. - */ - static class SingletonConsumer<T> implements Consumer<T> { - final AtomicReference<Consumer<T>> mAtomicRef; - - /** - * @param target the target consumer - **/ - SingletonConsumer(Consumer<T> target) { - mAtomicRef = new AtomicReference<>(target); - } - - @Override - public void accept(T t) { - final Consumer<T> consumer = mAtomicRef.getAndSet(null); - if (consumer != null) { - consumer.accept(t); - } - } - } -} diff --git a/core/java/android/view/SoundEffectConstants.java b/core/java/android/view/SoundEffectConstants.java index 8d891bbc2cfc..f177451783dc 100644 --- a/core/java/android/view/SoundEffectConstants.java +++ b/core/java/android/view/SoundEffectConstants.java @@ -16,12 +16,21 @@ package android.view; +import android.media.AudioManager; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; + +import java.util.Random; + /** - * Constants to be used to play sound effects via {@link View#playSoundEffect(int)} + * Constants to be used to play sound effects via {@link View#playSoundEffect(int)} */ public class SoundEffectConstants { private SoundEffectConstants() {} + private static final Random NAVIGATION_REPEAT_RANDOMIZER = new Random(); + private static int sLastNavigationRepeatSoundEffectId = -1; public static final int CLICK = 0; @@ -29,6 +38,14 @@ public class SoundEffectConstants { public static final int NAVIGATION_UP = 2; public static final int NAVIGATION_RIGHT = 3; public static final int NAVIGATION_DOWN = 4; + /** Sound effect for a repeatedly triggered navigation, e.g. due to long pressing a button */ + public static final int NAVIGATION_REPEAT_LEFT = 5; + /** @see #NAVIGATION_REPEAT_LEFT */ + public static final int NAVIGATION_REPEAT_UP = 6; + /** @see #NAVIGATION_REPEAT_LEFT */ + public static final int NAVIGATION_REPEAT_RIGHT = 7; + /** @see #NAVIGATION_REPEAT_LEFT */ + public static final int NAVIGATION_REPEAT_DOWN = 8; /** * Get the sonification constant for the focus directions. @@ -40,7 +57,7 @@ public class SoundEffectConstants { * @throws {@link IllegalArgumentException} when the passed direction is not one of the * documented values. */ - public static int getContantForFocusDirection(int direction) { + public static int getContantForFocusDirection(@View.FocusDirection int direction) { switch (direction) { case View.FOCUS_RIGHT: return SoundEffectConstants.NAVIGATION_RIGHT; @@ -56,4 +73,65 @@ public class SoundEffectConstants { throw new IllegalArgumentException("direction must be one of " + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT, FOCUS_FORWARD, FOCUS_BACKWARD}."); } + + /** + * Get the sonification constant for the focus directions + * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN}, + * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD} + * or {@link View#FOCUS_BACKWARD} + * @param repeating True if the user long-presses a direction + * @return The appropriate sonification constant + * @throws IllegalArgumentException when the passed direction is not one of the + * documented values. + */ + public static int getConstantForFocusDirection(@View.FocusDirection int direction, + boolean repeating) { + if (repeating) { + switch (direction) { + case View.FOCUS_RIGHT: + return SoundEffectConstants.NAVIGATION_REPEAT_RIGHT; + case View.FOCUS_FORWARD: + case View.FOCUS_DOWN: + return SoundEffectConstants.NAVIGATION_REPEAT_DOWN; + case View.FOCUS_LEFT: + return SoundEffectConstants.NAVIGATION_REPEAT_LEFT; + case View.FOCUS_BACKWARD: + case View.FOCUS_UP: + return SoundEffectConstants.NAVIGATION_REPEAT_UP; + } + throw new IllegalArgumentException("direction must be one of {FOCUS_UP, FOCUS_DOWN, " + + "FOCUS_LEFT, FOCUS_RIGHT, FOCUS_FORWARD, FOCUS_BACKWARD}."); + } else { + return getContantForFocusDirection(direction); + } + } + + /** + * @param effectId any of the effect ids defined in {@link SoundEffectConstants} + * @return true if the given effect id is a navigation repeat one + * @hide + */ + @VisibleForTesting(visibility = Visibility.PACKAGE) + public static boolean isNavigationRepeat(int effectId) { + return effectId == SoundEffectConstants.NAVIGATION_REPEAT_DOWN + || effectId == SoundEffectConstants.NAVIGATION_REPEAT_LEFT + || effectId == SoundEffectConstants.NAVIGATION_REPEAT_RIGHT + || effectId == SoundEffectConstants.NAVIGATION_REPEAT_UP; + } + + /** + * @return The next navigation repeat sound effect id, chosen at random in a non-repeating + * fashion + * @hide + */ + @VisibleForTesting(visibility = Visibility.PACKAGE) + public static int nextNavigationRepeatSoundEffectId() { + int next = NAVIGATION_REPEAT_RANDOMIZER.nextInt( + AudioManager.NUM_NAVIGATION_REPEAT_SOUND_EFFECTS - 1); + if (next >= sLastNavigationRepeatSoundEffectId) { + next++; + } + sLastNavigationRepeatSoundEffectId = next; + return AudioManager.getNthNavigationRepeatSoundEffect(next); + } } 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 3789324a038c..ebef4646b0d9 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -184,10 +184,10 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Queue; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; import java.util.function.Predicate; /** @@ -1484,7 +1484,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see #getScrollCaptureHint() * @see #setScrollCaptureHint(int) - * @hide */ public static final int SCROLL_CAPTURE_HINT_AUTO = 0; @@ -1495,7 +1494,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see #getScrollCaptureHint() * @see #setScrollCaptureHint(int) - * @hide */ public static final int SCROLL_CAPTURE_HINT_EXCLUDE = 0x1; @@ -1506,7 +1504,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see #getScrollCaptureHint() * @see #setScrollCaptureHint(int) - * @hide */ public static final int SCROLL_CAPTURE_HINT_INCLUDE = 0x2; @@ -1517,7 +1514,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see #getScrollCaptureHint() * @see #setScrollCaptureHint(int) - * @hide */ public static final int SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS = 0x4; @@ -30053,8 +30049,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Returns the current scroll capture hint for this view. * * @return the current scroll capture hint - * - * @hide */ @ScrollCaptureHint public int getScrollCaptureHint() { @@ -30067,8 +30061,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * scroll capture targets. * * @param hint the scrollCaptureHint flags value to set - * - * @hide */ public void setScrollCaptureHint(@ScrollCaptureHint int hint) { mPrivateFlags4 &= ~PFLAG4_SCROLL_CAPTURE_HINT_MASK; @@ -30088,10 +30080,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * setting a custom callback to help ensure it is selected as the target. * * @param callback the new callback to assign - * - * @hide */ - public void setScrollCaptureCallback(@Nullable ScrollCaptureCallback callback) { + public final void setScrollCaptureCallback(@Nullable ScrollCaptureCallback callback) { getListenerInfo().mScrollCaptureCallback = callback; } @@ -30110,29 +30100,54 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Dispatch a scroll capture search request down the view hierarchy. + * + * @param localVisibleRect the visible area of this ViewGroup in local coordinates, according to + * the parent + * @param windowOffset the offset of this view within the window + * @param targets accepts potential scroll capture targets; {@link Consumer#accept + * results.accept} may be called zero or more times on the calling + * thread before onScrollCaptureSearch returns + */ + public void dispatchScrollCaptureSearch( + @NonNull Rect localVisibleRect, @NonNull Point windowOffset, + @NonNull Consumer<ScrollCaptureTarget> targets) { + onScrollCaptureSearch(localVisibleRect, windowOffset, targets); + } + + /** * Called when scroll capture is requested, to search for appropriate content to scroll. If * applicable, this view adds itself to the provided list for consideration, subject to the * flags set by {@link #setScrollCaptureHint}. * * @param localVisibleRect the local visible rect of this view * @param windowOffset the offset of localVisibleRect within the window - * @param targets a queue which collects potential targets - * + * @param targets accepts potential scroll capture targets; {@link Consumer#accept + * results.accept} may be called zero or more times on the calling + * thread before onScrollCaptureSearch returns * @throws IllegalStateException if this view is not attached to a window - * @hide */ - public void dispatchScrollCaptureSearch(@NonNull Rect localVisibleRect, - @NonNull Point windowOffset, @NonNull Queue<ScrollCaptureTarget> targets) { + public void onScrollCaptureSearch(@NonNull Rect localVisibleRect, + @NonNull Point windowOffset, @NonNull Consumer<ScrollCaptureTarget> targets) { int hint = getScrollCaptureHint(); if ((hint & SCROLL_CAPTURE_HINT_EXCLUDE) != 0) { return; } + boolean rectIsVisible = true; + + // Apply clipBounds if present. + if (mClipBounds != null) { + rectIsVisible = localVisibleRect.intersect(mClipBounds); + } + if (!rectIsVisible) { + return; + } // Get a callback provided by the framework, library or application. ScrollCaptureCallback callback = (mListenerInfo == null) ? null : mListenerInfo.mScrollCaptureCallback; - // Try internal support for standard scrolling containers. + // Try framework support for standard scrolling containers. if (callback == null) { callback = createScrollCaptureCallbackInternal(localVisibleRect, windowOffset); } @@ -30142,7 +30157,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Add to the list for consideration Point offset = new Point(windowOffset.x, windowOffset.y); Rect rect = new Rect(localVisibleRect); - targets.add(new ScrollCaptureTarget(this, rect, offset, callback)); + targets.accept(new ScrollCaptureTarget(this, rect, offset, callback)); } } 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/ViewGroup.java b/core/java/android/view/ViewGroup.java index 37bea5821e42..38a59373554c 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -77,7 +77,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Queue; +import java.util.function.Consumer; import java.util.function.Predicate; /** @@ -7463,92 +7463,73 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** - * Offsets the given rectangle in parent's local coordinates into child's coordinate space - * and clips the result to the child View's bounds, padding and clipRect if appropriate. If the - * resulting rectangle is not empty, the request is forwarded to the child. - * <p> - * Note: This method does not account for any static View transformations which may be - * applied to the child view. - * - * @param child the child to dispatch to - * @param localVisibleRect the visible (clipped) area of this ViewGroup, in local coordinates - * @param windowOffset the offset of localVisibleRect within the window - * @param targets a queue to collect located targets - */ - private void dispatchTransformedScrollCaptureSearch(View child, Rect localVisibleRect, - Point windowOffset, Queue<ScrollCaptureTarget> targets) { - - // copy local visible rect for modification and dispatch - final Rect childVisibleRect = getTempRect(); - childVisibleRect.set(localVisibleRect); - - // transform to child coords - final Point childWindowOffset = getTempPoint(); - childWindowOffset.set(windowOffset.x, windowOffset.y); - - final int dx = child.mLeft - mScrollX; - final int dy = child.mTop - mScrollY; - - childVisibleRect.offset(-dx, -dy); - childWindowOffset.offset(dx, dy); - - boolean rectIsVisible = true; - final int width = mRight - mLeft; - final int height = mBottom - mTop; - - // Clip to child bounds - if (getClipChildren()) { - rectIsVisible = childVisibleRect.intersect(0, 0, child.getWidth(), child.getHeight()); - } - - // Clip to child padding. - if (rectIsVisible && (child instanceof ViewGroup) - && ((ViewGroup) child).getClipToPadding()) { - rectIsVisible = childVisibleRect.intersect( - child.mPaddingLeft, child.mPaddingTop, - child.getWidth() - child.mPaddingRight, - child.getHeight() - child.mPaddingBottom); - } - // Clip to child clipBounds. - if (rectIsVisible && child.mClipBounds != null) { - rectIsVisible = childVisibleRect.intersect(child.mClipBounds); - } - if (rectIsVisible) { - child.dispatchScrollCaptureSearch(childVisibleRect, childWindowOffset, targets); - } - } - - /** * Handle the scroll capture search request by checking this view if applicable, then to each * child view. * * @param localVisibleRect the visible area of this ViewGroup in local coordinates, according to * the parent * @param windowOffset the offset of this view within the window - * @param targets the collected list of scroll capture targets - * - * @hide + * @param targets accepts potential scroll capture targets; {@link Consumer#accept + * results.accept} may be called zero or more times on the calling + * thread before onScrollCaptureSearch returns */ @Override public void dispatchScrollCaptureSearch( @NonNull Rect localVisibleRect, @NonNull Point windowOffset, - @NonNull Queue<ScrollCaptureTarget> targets) { + @NonNull Consumer<ScrollCaptureTarget> targets) { + + // copy local visible rect for modification and dispatch + final Rect rect = getTempRect(); + rect.set(localVisibleRect); + + if (getClipToPadding()) { + rect.inset(mPaddingLeft, mPaddingTop, mPaddingRight, mPaddingBottom); + } // Dispatch to self first. super.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targets); - // Then dispatch to children, if not excluding descendants. - if ((getScrollCaptureHint() & SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS) == 0) { - final int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = getChildAt(i); - // Only visible views can be captured. - if (child.getVisibility() != View.VISIBLE) { - continue; - } - // Transform to child coords and dispatch - dispatchTransformedScrollCaptureSearch(child, localVisibleRect, windowOffset, - targets); + // Skip children if descendants excluded. + if ((getScrollCaptureHint() & SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS) != 0) { + return; + } + + final int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + // Only visible views can be captured. + if (child.getVisibility() != View.VISIBLE) { + continue; + } + // Offset the given rectangle (in parent's local coordinates) into child's coordinate + // space and clip the result to the child View's bounds, padding and clipRect as needed. + // If the resulting rectangle is not empty, the request is forwarded to the child. + + // copy local visible rect for modification and dispatch + final Rect childVisibleRect = getTempRect(); + childVisibleRect.set(localVisibleRect); + + // transform to child coords + final Point childWindowOffset = getTempPoint(); + childWindowOffset.set(windowOffset.x, windowOffset.y); + + final int dx = child.mLeft - mScrollX; + final int dy = child.mTop - mScrollY; + + childVisibleRect.offset(-dx, -dy); + childWindowOffset.offset(dx, dy); + + boolean rectIsVisible = true; + + // Clip to child bounds + if (getClipChildren()) { + rectIsVisible = childVisibleRect.intersect(0, 0, child.getWidth(), + child.getHeight()); + } + + // Clip to child padding. + if (rectIsVisible) { + child.dispatchScrollCaptureSearch(childVisibleRect, childWindowOffset, targets); } } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 144691d3eaa0..4f6679b2bc97 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -141,6 +141,7 @@ import android.util.AndroidRuntimeException; import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.EventLog; +import android.util.IndentingPrintWriter; import android.util.Log; import android.util.LongArray; import android.util.MergedConfiguration; @@ -202,6 +203,7 @@ import com.android.internal.view.SurfaceCallbackHelper; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; +import java.io.StringWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashSet; @@ -274,6 +276,11 @@ public final class ViewRootImpl implements ViewParent, */ private static final int CONTENT_CAPTURE_ENABLED_FALSE = 2; + /** + * Maximum time to wait for {@link View#dispatchScrollCaptureSearch} to complete. + */ + private static final int SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS = 2500; + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>(); @@ -323,6 +330,8 @@ public final class ViewRootImpl implements ViewParent, private boolean mForceDisableBLAST; private boolean mEnableTripleBuffering; + private boolean mFastScrollSoundEffectsEnabled; + /** * Signals that compatibility booleans have been initialized according to * target SDK versions. @@ -666,8 +675,6 @@ public final class ViewRootImpl implements ViewParent, private final InsetsController mInsetsController; private final ImeFocusController mImeFocusController; - private ScrollCaptureConnection mScrollCaptureConnection; - private boolean mIsSurfaceOpaque; private final BackgroundBlurDrawable.Aggregator mBlurRegionAggregator = @@ -681,12 +688,6 @@ public final class ViewRootImpl implements ViewParent, return mImeFocusController; } - /** @return The current {@link ScrollCaptureConnection} for this instance, if any is active. */ - @Nullable - public ScrollCaptureConnection getScrollCaptureConnection() { - return mScrollCaptureConnection; - } - private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker(); private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection; @@ -728,6 +729,8 @@ public final class ViewRootImpl implements ViewParent, private HashSet<ScrollCaptureCallback> mRootScrollCaptureCallbacks; + private long mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS; + /** * Increment this value when the surface has been replaced. */ @@ -813,6 +816,10 @@ public final class ViewRootImpl implements ViewParent, loadSystemProperties(); mImeFocusController = new ImeFocusController(this); + AudioManager audioManager = mContext.getSystemService(AudioManager.class); + mFastScrollSoundEffectsEnabled = audioManager.areNavigationRepeatSoundEffectsEnabled(); + + mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS; } public static void addFirstDrawHandler(Runnable callback) { @@ -6081,8 +6088,10 @@ public final class ViewRootImpl implements ViewParent, v, mTempRect); } if (v.requestFocus(direction, mTempRect)) { - playSoundEffect(SoundEffectConstants - .getContantForFocusDirection(direction)); + boolean isFastScrolling = event.getRepeatCount() > 0; + playSoundEffect( + SoundEffectConstants.getConstantForFocusDirection(direction, + isFastScrolling)); return true; } } @@ -7743,20 +7752,31 @@ public final class ViewRootImpl implements ViewParent, try { final AudioManager audioManager = getAudioManager(); + if (mFastScrollSoundEffectsEnabled + && SoundEffectConstants.isNavigationRepeat(effectId)) { + audioManager.playSoundEffect( + SoundEffectConstants.nextNavigationRepeatSoundEffectId()); + return; + } + switch (effectId) { case SoundEffectConstants.CLICK: audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK); return; case SoundEffectConstants.NAVIGATION_DOWN: + case SoundEffectConstants.NAVIGATION_REPEAT_DOWN: audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN); return; case SoundEffectConstants.NAVIGATION_LEFT: + case SoundEffectConstants.NAVIGATION_REPEAT_LEFT: audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT); return; case SoundEffectConstants.NAVIGATION_RIGHT: + case SoundEffectConstants.NAVIGATION_REPEAT_RIGHT: audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT); return; case SoundEffectConstants.NAVIGATION_UP: + case SoundEffectConstants.NAVIGATION_REPEAT_UP: audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP); return; default: @@ -9206,9 +9226,9 @@ public final class ViewRootImpl implements ViewParent, * Collect and include any ScrollCaptureCallback instances registered with the window. * * @see #addScrollCaptureCallback(ScrollCaptureCallback) - * @param targets the search queue for targets + * @param results an object to collect the results of the search */ - private void collectRootScrollCaptureTargets(Queue<ScrollCaptureTarget> targets) { + private void collectRootScrollCaptureTargets(ScrollCaptureSearchResults results) { if (mRootScrollCaptureCallbacks == null) { return; } @@ -9216,26 +9236,45 @@ public final class ViewRootImpl implements ViewParent, // Add to the list for consideration Point offset = new Point(mView.getLeft(), mView.getTop()); Rect rect = new Rect(0, 0, mView.getWidth(), mView.getHeight()); - targets.add(new ScrollCaptureTarget(mView, rect, offset, cb)); + results.addTarget(new ScrollCaptureTarget(mView, rect, offset, cb)); } } /** - * Handles an inbound request for scroll capture from the system. If a client is not already - * active, a search will be dispatched through the view tree to locate scrolling content. + * Update the timeout for scroll capture requests. Only affects this view root. + * The default value is {@link #SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS}. + * + * @param timeMillis the new timeout in milliseconds + */ + public void setScrollCaptureRequestTimeout(int timeMillis) { + mScrollCaptureRequestTimeout = timeMillis; + } + + /** + * Get the current timeout for scroll capture requests. + * + * @return the timeout in milliseconds + */ + public long getScrollCaptureRequestTimeout() { + return mScrollCaptureRequestTimeout; + } + + /** + * Handles an inbound request for scroll capture from the system. A search will be + * dispatched through the view tree to locate scrolling content. * <p> - * Either {@link IScrollCaptureCallbacks#onConnected(IScrollCaptureConnection, Rect, - * Point)} or {@link IScrollCaptureCallbacks#onUnavailable()} will be returned - * depending on the results of the search. + * A call to {@link IScrollCaptureCallbacks#onScrollCaptureResponse(ScrollCaptureResponse)} + * will follow. * * @param callbacks to receive responses - * @see ScrollCaptureTargetResolver + * @see ScrollCaptureTargetSelector */ public void handleScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) { - LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>(); + ScrollCaptureSearchResults results = + new ScrollCaptureSearchResults(mContext.getMainExecutor()); // Window (root) level callbacks - collectRootScrollCaptureTargets(targetList); + collectRootScrollCaptureTargets(results); // Search through View-tree View rootView = getView(); @@ -9243,58 +9282,70 @@ public final class ViewRootImpl implements ViewParent, Point point = new Point(); Rect rect = new Rect(0, 0, rootView.getWidth(), rootView.getHeight()); getChildVisibleRect(rootView, rect, point); - rootView.dispatchScrollCaptureSearch(rect, point, targetList); + rootView.dispatchScrollCaptureSearch(rect, point, results::addTarget); } - - // No-op path. Scroll capture not offered for this window. - if (targetList.isEmpty()) { - dispatchScrollCaptureSearchResult(callbacks, null); - return; + Runnable onComplete = () -> dispatchScrollCaptureSearchResult(callbacks, results); + results.setOnCompleteListener(onComplete); + if (!results.isComplete()) { + mHandler.postDelayed(results::finish, getScrollCaptureRequestTimeout()); } - - // Request scrollBounds from each of the targets. - // Continues with the consumer once all responses are consumed, or the timeout expires. - ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetList); - resolver.start(mHandler, 1000, - (selected) -> dispatchScrollCaptureSearchResult(callbacks, selected)); } /** Called by {@link #handleScrollCaptureRequest} when a result is returned */ private void dispatchScrollCaptureSearchResult( @NonNull IScrollCaptureCallbacks callbacks, - @Nullable ScrollCaptureTarget selectedTarget) { + @NonNull ScrollCaptureSearchResults results) { + + ScrollCaptureTarget selectedTarget = results.getTopResult(); + + ScrollCaptureResponse.Builder response = new ScrollCaptureResponse.Builder(); + response.setWindowTitle(getTitle().toString()); + + StringWriter writer = new StringWriter(); + IndentingPrintWriter pw = new IndentingPrintWriter(writer); + results.dump(pw); + pw.flush(); + response.addMessage(writer.toString()); - // If timeout or no eligible targets found. if (selectedTarget == null) { + response.setDescription("No scrollable targets found in window"); try { - if (DEBUG_SCROLL_CAPTURE) { - Log.d(TAG, "scrollCaptureSearch returned no targets available."); - } - callbacks.onUnavailable(); + callbacks.onScrollCaptureResponse(response.build()); } catch (RemoteException e) { - if (DEBUG_SCROLL_CAPTURE) { - Log.w(TAG, "Failed to send scroll capture search result.", e); - } + Log.e(TAG, "Failed to send scroll capture search result", e); } return; } - // Create a client instance and return it to the caller - mScrollCaptureConnection = new ScrollCaptureConnection(selectedTarget, callbacks); + response.setDescription("Connected"); + + // Compute area covered by scrolling content within window + Rect boundsInWindow = new Rect(); + View containingView = selectedTarget.getContainingView(); + containingView.getLocationInWindow(mAttachInfo.mTmpLocation); + boundsInWindow.set(selectedTarget.getScrollBounds()); + boundsInWindow.offset(mAttachInfo.mTmpLocation[0], mAttachInfo.mTmpLocation[1]); + response.setBoundsInWindow(boundsInWindow); + + // Compute the area on screen covered by the window + Rect boundsOnScreen = new Rect(); + mView.getLocationOnScreen(mAttachInfo.mTmpLocation); + boundsOnScreen.set(0, 0, mView.getWidth(), mView.getHeight()); + boundsOnScreen.offset(mAttachInfo.mTmpLocation[0], mAttachInfo.mTmpLocation[1]); + response.setWindowBounds(boundsOnScreen); + + // Create a connection and return it to the caller + ScrollCaptureConnection connection = new ScrollCaptureConnection( + mView.getContext().getMainExecutor(), selectedTarget, callbacks); + response.setConnection(connection); + try { - if (DEBUG_SCROLL_CAPTURE) { - Log.d(TAG, "scrollCaptureSearch returning client: " + getScrollCaptureConnection()); - } - callbacks.onConnected( - mScrollCaptureConnection, - selectedTarget.getScrollBounds(), - selectedTarget.getPositionInWindow()); + callbacks.onScrollCaptureResponse(response.build()); } catch (RemoteException e) { if (DEBUG_SCROLL_CAPTURE) { - Log.w(TAG, "Failed to send scroll capture search result.", e); + Log.w(TAG, "Failed to send scroll capture search response.", e); } - mScrollCaptureConnection.disconnect(); - mScrollCaptureConnection = null; + connection.close(); } } diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 4ecdd78f5a42..221b3346df58 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -2621,7 +2621,6 @@ public abstract class Window { * callback with the root view of the window. * * @param callback the callback to add - * @hide */ public void registerScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) { } @@ -2630,7 +2629,6 @@ public abstract class Window { * Unregisters a {@link ScrollCaptureCallback} previously registered with this window. * * @param callback the callback to remove - * @hide */ public void unregisterScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) { } diff --git a/core/java/android/window/ClientWindowFrames.java b/core/java/android/window/ClientWindowFrames.java index e22a5eb9fe7b..acf9882e6658 100644 --- a/core/java/android/window/ClientWindowFrames.java +++ b/core/java/android/window/ClientWindowFrames.java @@ -58,9 +58,9 @@ public class ClientWindowFrames implements Parcelable { /** Needed for AIDL out parameters. */ public void readFromParcel(Parcel in) { - frame.set(Rect.CREATOR.createFromParcel(in)); - displayFrame.set(Rect.CREATOR.createFromParcel(in)); - backdropFrame.set(Rect.CREATOR.createFromParcel(in)); + frame.readFromParcel(in); + displayFrame.readFromParcel(in); + backdropFrame.readFromParcel(in); } @Override 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/com/android/internal/graphics/palette/CentroidProvider.java b/core/java/com/android/internal/graphics/palette/CentroidProvider.java new file mode 100644 index 000000000000..5fcfcbab3159 --- /dev/null +++ b/core/java/com/android/internal/graphics/palette/CentroidProvider.java @@ -0,0 +1,38 @@ +/* + * 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.annotation.ColorInt; + +interface CentroidProvider { + /** + * @return 3 dimensions representing the color + */ + float[] getCentroid(@ColorInt int color); + + /** + * @param centroid 3 dimensions representing the color + * @return 32-bit ARGB representation + */ + @ColorInt + int getColor(float[] centroid); + + /** + * 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/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 1f8ffe08a257..62e1ee19b25e 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -7965,16 +7965,14 @@ public class BatteryStatsImpl extends BatteryStats { /** Adds the given energy to the given standard energy bucket for this uid. */ private void addEnergyToStandardBucketLocked(long energyDeltaUJ, - @StandardEnergyBucket int energyBucket, boolean accumulate) { + @StandardEnergyBucket int energyBucket) { getOrCreateMeasuredEnergyStatsLocked() - .updateStandardBucket(energyBucket, energyDeltaUJ, accumulate); + .updateStandardBucket(energyBucket, energyDeltaUJ); } /** Adds the given energy to the given custom energy bucket for this uid. */ - private void addEnergyToCustomBucketLocked(long energyDeltaUJ, int energyBucket, - boolean accumulate) { - getOrCreateMeasuredEnergyStatsLocked() - .updateCustomBucket(energyBucket, energyDeltaUJ, accumulate); + private void addEnergyToCustomBucketLocked(long energyDeltaUJ, int energyBucket) { + getOrCreateMeasuredEnergyStatsLocked().updateCustomBucket(energyBucket, energyDeltaUJ); } /** @@ -12468,7 +12466,7 @@ public class BatteryStatsImpl extends BatteryStats { return; } - mGlobalMeasuredEnergyStats.updateStandardBucket(energyBucket, energyUJ, true); + mGlobalMeasuredEnergyStats.updateStandardBucket(energyBucket, energyUJ); // Now we blame individual apps, but only if the display was ON. if (energyBucket != MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON) { @@ -12506,7 +12504,7 @@ public class BatteryStatsImpl extends BatteryStats { final long appDisplayEnergyMJ = (totalDisplayEnergyMJ * fgTimeMs + (totalFgTimeMs / 2)) / totalFgTimeMs; - uid.addEnergyToStandardBucketLocked(appDisplayEnergyMJ * 1000, energyBucket, true); + uid.addEnergyToStandardBucketLocked(appDisplayEnergyMJ * 1000, energyBucket); // To mitigate round-off errors, remove this app from numerator & denominator totals totalDisplayEnergyMJ -= appDisplayEnergyMJ; @@ -12520,6 +12518,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) { @@ -12532,7 +12531,7 @@ public class BatteryStatsImpl extends BatteryStats { if (mGlobalMeasuredEnergyStats == null) return; if (!mOnBatteryInternal || mIgnoreNextExternalStats || totalEnergyUJ <= 0) return; - mGlobalMeasuredEnergyStats.updateCustomBucket(customEnergyBucket, totalEnergyUJ, true); + mGlobalMeasuredEnergyStats.updateCustomBucket(customEnergyBucket, totalEnergyUJ); if (uidEnergies == null) return; final int numUids = uidEnergies.size(); @@ -12540,10 +12539,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); + } 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/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java index eef9fa74e83a..0163acc295fb 100644 --- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java +++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java @@ -22,10 +22,12 @@ import android.os.BatteryStats; import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; import android.os.Bundle; -import android.os.SystemClock; +import android.os.UidBatteryConsumer; import android.os.UserHandle; import android.util.SparseArray; +import com.android.internal.annotations.VisibleForTesting; + import java.util.ArrayList; import java.util.List; @@ -85,7 +87,7 @@ public class BatteryUsageStatsProvider { } /** - * Returns a snapshot of battery attribution data. + * Returns snapshots of battery attribution data, one per supplied query. */ public List<BatteryUsageStats> getBatteryUsageStats(List<BatteryUsageStatsQuery> queries) { @@ -112,12 +114,19 @@ public class BatteryUsageStatsProvider { return results; } - private BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) { + /** + * Returns a snapshot of battery attribution data. + */ + @VisibleForTesting + public BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) { + final long realtimeUs = mStats.mClocks.elapsedRealtime() * 1000; + final long uptimeUs = mStats.mClocks.uptimeMillis() * 1000; + final long[] customMeasuredEnergiesMicroJoules = mStats.getCustomMeasuredEnergiesMicroJoules(); final int customPowerComponentCount = customMeasuredEnergiesMicroJoules != null - ? customMeasuredEnergiesMicroJoules.length - : 0; + ? customMeasuredEnergiesMicroJoules.length + : 0; // TODO(b/174186358): read extra time component number from configuration final int customTimeComponentCount = 0; @@ -128,12 +137,14 @@ public class BatteryUsageStatsProvider { SparseArray<? extends BatteryStats.Uid> uidStats = mStats.getUidStats(); for (int i = uidStats.size() - 1; i >= 0; i--) { - batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uidStats.valueAt(i)); + final BatteryStats.Uid uid = uidStats.valueAt(i); + batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid) + .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, + getProcessBackgroundTimeMs(uid, realtimeUs)) + .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, + getProcessForegroundTimeMs(uid, realtimeUs)); } - final long realtimeUs = SystemClock.elapsedRealtime() * 1000; - final long uptimeUs = SystemClock.uptimeMillis() * 1000; - final List<PowerCalculator> powerCalculators = getPowerCalculators(); for (int i = 0, count = powerCalculators.size(); i < count; i++) { PowerCalculator powerCalculator = powerCalculators.get(i); @@ -143,4 +154,35 @@ public class BatteryUsageStatsProvider { return batteryUsageStatsBuilder.build(); } + + private long getProcessForegroundTimeMs(BatteryStats.Uid uid, long realtimeUs) { + final long topStateDurationMs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_TOP, + realtimeUs, BatteryStats.STATS_SINCE_CHARGED) / 1000; + + long foregroundActivityDurationMs = 0; + final BatteryStats.Timer foregroundActivityTimer = uid.getForegroundActivityTimer(); + if (foregroundActivityTimer != null) { + foregroundActivityDurationMs = foregroundActivityTimer.getTotalTimeLocked(realtimeUs, + BatteryStats.STATS_SINCE_CHARGED) / 1000; + } + + // Use the min value of STATE_TOP time and foreground activity time, since both of these + // times are imprecise + final long foregroundDurationMs = Math.min(topStateDurationMs, + foregroundActivityDurationMs); + + long foregroundServiceDurationMs = 0; + final BatteryStats.Timer foregroundServiceTimer = uid.getForegroundServiceTimer(); + if (foregroundServiceTimer != null) { + foregroundServiceDurationMs = foregroundServiceTimer.getTotalTimeLocked(realtimeUs, + BatteryStats.STATS_SINCE_CHARGED) / 1000; + } + + return foregroundDurationMs + foregroundServiceDurationMs; + } + + private long getProcessBackgroundTimeMs(BatteryStats.Uid uid, long realtimeUs) { + return uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_BACKGROUND, realtimeUs, + BatteryStats.STATS_SINCE_CHARGED) / 1000; + } } diff --git a/core/java/com/android/internal/os/UsageBasedPowerEstimator.java b/core/java/com/android/internal/os/UsageBasedPowerEstimator.java index 5910b61f0f6f..fd5201431eab 100644 --- a/core/java/com/android/internal/os/UsageBasedPowerEstimator.java +++ b/core/java/com/android/internal/os/UsageBasedPowerEstimator.java @@ -32,6 +32,10 @@ public class UsageBasedPowerEstimator { mAveragePowerMahPerMs = averagePowerMilliAmp / MILLIS_IN_HOUR; } + public boolean isSupported() { + return mAveragePowerMahPerMs != 0; + } + /** * Given a {@link BatteryStats.Timer}, returns the accumulated duration. */ diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java index 63763f76c6ed..98f613fc1c40 100644 --- a/core/java/com/android/internal/os/WifiPowerCalculator.java +++ b/core/java/com/android/internal/os/WifiPowerCalculator.java @@ -15,8 +15,13 @@ */ package com.android.internal.os; +import android.os.BatteryConsumer; import android.os.BatteryStats; +import android.os.BatteryUsageStats; +import android.os.BatteryUsageStatsQuery; import android.os.Process; +import android.os.SystemBatteryConsumer; +import android.os.UidBatteryConsumer; import android.os.UserHandle; import android.util.Log; import android.util.SparseArray; @@ -30,25 +35,93 @@ import java.util.List; public class WifiPowerCalculator extends PowerCalculator { private static final boolean DEBUG = BatteryStatsHelper.DEBUG; private static final String TAG = "WifiPowerCalculator"; - private final double mIdleCurrentMa; - private final double mTxCurrentMa; - private final double mRxCurrentMa; - private final PowerProfile mPowerProfile; + private final UsageBasedPowerEstimator mIdlePowerEstimator; + private final UsageBasedPowerEstimator mTxPowerEstimator; + private final UsageBasedPowerEstimator mRxPowerEstimator; + private final UsageBasedPowerEstimator mPowerOnPowerEstimator; + private final UsageBasedPowerEstimator mScanPowerEstimator; + private final UsageBasedPowerEstimator mBatchScanPowerEstimator; private final boolean mHasWifiPowerController; - private double mTotalAppPowerDrain = 0; - private long mTotalAppRunningTime = 0; - private WifiPowerEstimator mWifiPowerEstimator; - private boolean mHasWifiPowerReporting; + private final double mWifiPowerPerPacket; + + private static class PowerDurationAndTraffic { + public double powerMah; + public long durationMs; + + public long wifiRxPackets; + public long wifiTxPackets; + public long wifiRxBytes; + public long wifiTxBytes; + } public WifiPowerCalculator(PowerProfile profile) { - mPowerProfile = profile; + mPowerOnPowerEstimator = new UsageBasedPowerEstimator( + profile.getAveragePower(PowerProfile.POWER_WIFI_ON)); + mScanPowerEstimator = new UsageBasedPowerEstimator( + profile.getAveragePower(PowerProfile.POWER_WIFI_SCAN)); + mBatchScanPowerEstimator = new UsageBasedPowerEstimator( + profile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN)); + mIdlePowerEstimator = new UsageBasedPowerEstimator( + profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE)); + mTxPowerEstimator = new UsageBasedPowerEstimator( + profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX)); + mRxPowerEstimator = new UsageBasedPowerEstimator( + profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX)); + + mWifiPowerPerPacket = getWifiPowerPerPacket(profile); + + mHasWifiPowerController = + mIdlePowerEstimator.isSupported() && mTxPowerEstimator.isSupported() + && mRxPowerEstimator.isSupported(); + } + + @Override + public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { + + // batteryStats.hasWifiActivityReporting can change if we get energy data at a later point, + // so always check this field. + final boolean hasWifiPowerReporting = + mHasWifiPowerController && batteryStats.hasWifiActivityReporting(); + + final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder = + builder.getOrCreateSystemBatteryConsumerBuilder( + SystemBatteryConsumer.DRAIN_TYPE_WIFI); - mIdleCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE); - mTxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX); - mRxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX); + long totalAppDurationMs = 0; + double totalAppPowerMah = 0; + final PowerDurationAndTraffic powerDurationAndTraffic = new PowerDurationAndTraffic(); + final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = + builder.getUidBatteryConsumerBuilders(); + for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { + final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); + calculateApp(powerDurationAndTraffic, app.getBatteryStatsUid(), rawRealtimeUs, + BatteryStats.STATS_SINCE_CHARGED, + hasWifiPowerReporting); - mHasWifiPowerController = mIdleCurrentMa != 0 && mTxCurrentMa != 0 && mRxCurrentMa != 0; - mWifiPowerEstimator = new WifiPowerEstimator(mPowerProfile); + totalAppDurationMs += powerDurationAndTraffic.durationMs; + totalAppPowerMah += powerDurationAndTraffic.powerMah; + + app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI, + powerDurationAndTraffic.durationMs); + app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI, + powerDurationAndTraffic.powerMah); + + if (app.getUid() == Process.WIFI_UID) { + systemBatteryConsumerBuilder.addUidBatteryConsumer(app); + app.excludeFromBatteryUsageStats(); + } + } + + calculateRemaining(powerDurationAndTraffic, batteryStats, rawRealtimeUs, + BatteryStats.STATS_SINCE_CHARGED, + hasWifiPowerReporting, totalAppDurationMs, totalAppPowerMah); + + systemBatteryConsumerBuilder + .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI, + powerDurationAndTraffic.durationMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI, + powerDurationAndTraffic.powerMah); } /** @@ -64,100 +137,151 @@ public class WifiPowerCalculator extends PowerCalculator { // batteryStats.hasWifiActivityReporting can change if we get energy data at a later point, // so always check this field. - mHasWifiPowerReporting = mHasWifiPowerController && batteryStats.hasWifiActivityReporting(); - - super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers); + final boolean hasWifiPowerReporting = + mHasWifiPowerController && batteryStats.hasWifiActivityReporting(); - BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0); - calculateRemaining(bs, batteryStats, rawRealtimeUs, rawUptimeUs, statsType); + final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0); + long totalAppDurationMs = 0; + double totalAppPowerMah = 0; + final PowerDurationAndTraffic powerDurationAndTraffic = new PowerDurationAndTraffic(); for (int i = sippers.size() - 1; i >= 0; i--) { - BatterySipper app = sippers.get(i); - if (app.getUid() == Process.WIFI_UID) { - if (DEBUG) Log.d(TAG, "WiFi adding sipper " + app + ": cpu=" + app.cpuTimeMs); - app.isAggregated = true; - bs.add(app); + final BatterySipper app = sippers.get(i); + if (app.drainType == BatterySipper.DrainType.APP) { + calculateApp(powerDurationAndTraffic, app.uidObj, rawRealtimeUs, statsType, + hasWifiPowerReporting); + + totalAppDurationMs += powerDurationAndTraffic.durationMs; + totalAppPowerMah += powerDurationAndTraffic.powerMah; + + app.wifiPowerMah = powerDurationAndTraffic.powerMah; + app.wifiRunningTimeMs = powerDurationAndTraffic.durationMs; + app.wifiRxBytes = powerDurationAndTraffic.wifiRxBytes; + app.wifiRxPackets = powerDurationAndTraffic.wifiRxPackets; + app.wifiTxBytes = powerDurationAndTraffic.wifiTxBytes; + app.wifiTxPackets = powerDurationAndTraffic.wifiTxPackets; + if (app.getUid() == Process.WIFI_UID) { + if (DEBUG) Log.d(TAG, "WiFi adding sipper " + app + ": cpu=" + app.cpuTimeMs); + app.isAggregated = true; + bs.add(app); + } } } + + calculateRemaining(powerDurationAndTraffic, batteryStats, rawRealtimeUs, statsType, + hasWifiPowerReporting, totalAppDurationMs, totalAppPowerMah); + + bs.wifiRunningTimeMs += powerDurationAndTraffic.durationMs; + bs.wifiPowerMah += powerDurationAndTraffic.powerMah; + if (bs.sumPower() > 0) { sippers.add(bs); } } - @Override - protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, - long rawUptimeUs, int statsType) { - if (!mHasWifiPowerReporting) { - mWifiPowerEstimator.calculateApp(app, u, rawRealtimeUs, rawUptimeUs, statsType); - return; - } - - final BatteryStats.ControllerActivityCounter counter = u.getWifiControllerActivity(); - if (counter == null) { - return; - } - - final long idleTime = counter.getIdleTimeCounter().getCountLocked(statsType); - final long txTime = counter.getTxTimeCounters()[0].getCountLocked(statsType); - final long rxTime = counter.getRxTimeCounter().getCountLocked(statsType); - app.wifiRunningTimeMs = idleTime + rxTime + txTime; - mTotalAppRunningTime += app.wifiRunningTimeMs; - - app.wifiPowerMah = - ((idleTime * mIdleCurrentMa) + (txTime * mTxCurrentMa) + (rxTime * mRxCurrentMa)) - / (1000 * 60 * 60); - mTotalAppPowerDrain += app.wifiPowerMah; - - app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA, + private void calculateApp(PowerDurationAndTraffic powerDurationAndTraffic, BatteryStats.Uid u, + long rawRealtimeUs, + int statsType, boolean hasWifiPowerReporting) { + powerDurationAndTraffic.wifiRxPackets = u.getNetworkActivityPackets( + BatteryStats.NETWORK_WIFI_RX_DATA, statsType); - app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA, + powerDurationAndTraffic.wifiTxPackets = u.getNetworkActivityPackets( + BatteryStats.NETWORK_WIFI_TX_DATA, statsType); - app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA, + powerDurationAndTraffic.wifiRxBytes = u.getNetworkActivityBytes( + BatteryStats.NETWORK_WIFI_RX_DATA, statsType); - app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA, + powerDurationAndTraffic.wifiTxBytes = u.getNetworkActivityBytes( + BatteryStats.NETWORK_WIFI_TX_DATA, statsType); - if (DEBUG && app.wifiPowerMah != 0) { - Log.d(TAG, "UID " + u.getUid() + ": idle=" + idleTime + "ms rx=" + rxTime + "ms tx=" + - txTime + "ms power=" + formatCharge(app.wifiPowerMah)); - } - } + if (hasWifiPowerReporting) { + final BatteryStats.ControllerActivityCounter counter = u.getWifiControllerActivity(); + if (counter != null) { + final long idleTime = counter.getIdleTimeCounter().getCountLocked(statsType); + final long txTime = counter.getTxTimeCounters()[0].getCountLocked(statsType); + final long rxTime = counter.getRxTimeCounter().getCountLocked(statsType); + + powerDurationAndTraffic.durationMs = idleTime + rxTime + txTime; + powerDurationAndTraffic.powerMah = mIdlePowerEstimator.calculatePower(idleTime) + + mTxPowerEstimator.calculatePower(txTime) + + mRxPowerEstimator.calculatePower(rxTime); - private void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, - long rawUptimeUs, int statsType) { - if (!mHasWifiPowerReporting) { - mWifiPowerEstimator.calculateRemaining(app, stats, rawRealtimeUs, rawUptimeUs, - statsType); - return; + if (DEBUG && powerDurationAndTraffic.powerMah != 0) { + Log.d(TAG, "UID " + u.getUid() + ": idle=" + idleTime + "ms rx=" + rxTime + + "ms tx=" + txTime + "ms power=" + formatCharge( + powerDurationAndTraffic.powerMah)); + } + } + } else { + final double wifiPacketPower = ( + powerDurationAndTraffic.wifiRxPackets + powerDurationAndTraffic.wifiTxPackets) + * mWifiPowerPerPacket; + final long wifiRunningTime = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000; + final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000; + long batchScanTimeMs = 0; + for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) { + batchScanTimeMs += u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000; + } + + powerDurationAndTraffic.durationMs = wifiRunningTime; + powerDurationAndTraffic.powerMah = wifiPacketPower + + mPowerOnPowerEstimator.calculatePower(wifiRunningTime) + + mScanPowerEstimator.calculatePower(wifiScanTimeMs) + + mBatchScanPowerEstimator.calculatePower(batchScanTimeMs); + + if (DEBUG && powerDurationAndTraffic.powerMah != 0) { + Log.d(TAG, "UID " + u.getUid() + ": power=" + formatCharge( + powerDurationAndTraffic.powerMah)); + } } + } - final BatteryStats.ControllerActivityCounter counter = stats.getWifiControllerActivity(); + private void calculateRemaining(PowerDurationAndTraffic powerDurationAndTraffic, + BatteryStats stats, long rawRealtimeUs, + int statsType, boolean hasWifiPowerReporting, long totalAppDurationMs, + double totalAppPowerMah) { + long totalDurationMs; + double totalPowerMah; + if (hasWifiPowerReporting) { + final BatteryStats.ControllerActivityCounter counter = + stats.getWifiControllerActivity(); - final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(statsType); - final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(statsType); - final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(statsType); + final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(statsType); + final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(statsType); + final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(statsType); - app.wifiRunningTimeMs = Math.max(0, - (idleTimeMs + rxTimeMs + txTimeMs) - mTotalAppRunningTime); + totalDurationMs = idleTimeMs + rxTimeMs + txTimeMs; - double powerDrainMah = counter.getPowerCounter().getCountLocked(statsType) - / (double) (1000 * 60 * 60); - if (powerDrainMah == 0) { - // Some controllers do not report power drain, so we can calculate it here. - powerDrainMah = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa) - + (rxTimeMs * mRxCurrentMa)) / (1000 * 60 * 60); + totalPowerMah = + counter.getPowerCounter().getCountLocked(statsType) / (double) (1000 * 60 * 60); + if (totalPowerMah == 0) { + // Some controllers do not report power drain, so we can calculate it here. + totalPowerMah = mIdlePowerEstimator.calculatePower(idleTimeMs) + + mTxPowerEstimator.calculatePower(txTimeMs) + + mRxPowerEstimator.calculatePower(rxTimeMs); + } + } else { + totalDurationMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType) / 1000; + totalPowerMah = mPowerOnPowerEstimator.calculatePower(totalDurationMs); } - app.wifiPowerMah = Math.max(0, powerDrainMah - mTotalAppPowerDrain); + + powerDurationAndTraffic.durationMs = Math.max(0, totalDurationMs - totalAppDurationMs); + powerDurationAndTraffic.powerMah = Math.max(0, totalPowerMah - totalAppPowerMah); if (DEBUG) { - Log.d(TAG, "left over WiFi power: " + formatCharge(app.wifiPowerMah)); + Log.d(TAG, "left over WiFi power: " + formatCharge(powerDurationAndTraffic.powerMah)); } } - @Override - public void reset() { - mTotalAppPowerDrain = 0; - mTotalAppRunningTime = 0; - mWifiPowerEstimator.reset(); + /** + * Return estimated power per Wi-Fi packet in mAh/packet where 1 packet = 2 KB. + */ + private static double getWifiPowerPerPacket(PowerProfile profile) { + // TODO(b/179392913): Extract average bit rates from system + final long wifiBps = 1000000; + final double averageWifiActivePower = + profile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE) / 3600; + return averageWifiActivePower / (((double) wifiBps) / 8 / 2048); } } diff --git a/core/java/com/android/internal/os/WifiPowerEstimator.java b/core/java/com/android/internal/os/WifiPowerEstimator.java deleted file mode 100644 index d0a105cfd958..000000000000 --- a/core/java/com/android/internal/os/WifiPowerEstimator.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 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. - */ -package com.android.internal.os; - -import android.os.BatteryStats; -import android.os.Process; -import android.os.UserHandle; -import android.util.Log; -import android.util.SparseArray; - -import java.util.List; - -/** - * Estimates WiFi power usage based on timers in BatteryStats. - */ -public class WifiPowerEstimator extends PowerCalculator { - private static final boolean DEBUG = BatteryStatsHelper.DEBUG; - private static final String TAG = "WifiPowerEstimator"; - private final double mWifiPowerPerPacket; - private final double mWifiPowerOn; - private final double mWifiPowerScan; - private final double mWifiPowerBatchScan; - private long mTotalAppWifiRunningTimeMs = 0; - - public WifiPowerEstimator(PowerProfile profile) { - mWifiPowerPerPacket = getWifiPowerPerPacket(profile); - mWifiPowerOn = profile.getAveragePower(PowerProfile.POWER_WIFI_ON); - mWifiPowerScan = profile.getAveragePower(PowerProfile.POWER_WIFI_SCAN); - mWifiPowerBatchScan = profile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN); - } - - @Override - public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, - long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { - super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers); - - BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0); - calculateRemaining(bs, batteryStats, rawRealtimeUs, rawUptimeUs, statsType); - - for (int i = sippers.size() - 1; i >= 0; i--) { - BatterySipper app = sippers.get(i); - if (app.getUid() == Process.WIFI_UID) { - if (DEBUG) Log.d(TAG, "WiFi adding sipper " + app + ": cpu=" + app.cpuTimeMs); - app.isAggregated = true; - bs.add(app); - } - } - if (bs.sumPower() > 0) { - sippers.add(bs); - } - } - - /** - * Return estimated power per Wi-Fi packet in mAh/packet where 1 packet = 2 KB. - */ - private static double getWifiPowerPerPacket(PowerProfile profile) { - final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system - final double WIFI_POWER = profile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE) - / 3600; - return WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048); - } - - @Override - protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, - long rawUptimeUs, int statsType) { - app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA, - statsType); - app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA, - statsType); - app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA, - statsType); - app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA, - statsType); - - final double wifiPacketPower = (app.wifiRxPackets + app.wifiTxPackets) - * mWifiPowerPerPacket; - - app.wifiRunningTimeMs = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000; - mTotalAppWifiRunningTimeMs += app.wifiRunningTimeMs; - final double wifiLockPower = (app.wifiRunningTimeMs * mWifiPowerOn) / (1000*60*60); - - final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000; - final double wifiScanPower = (wifiScanTimeMs * mWifiPowerScan) / (1000*60*60); - - double wifiBatchScanPower = 0; - for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) { - final long batchScanTimeMs = - u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000; - final double batchScanPower = (batchScanTimeMs * mWifiPowerBatchScan) / (1000*60*60); - wifiBatchScanPower += batchScanPower; - } - - app.wifiPowerMah = wifiPacketPower + wifiLockPower + wifiScanPower + wifiBatchScanPower; - if (DEBUG && app.wifiPowerMah != 0) { - Log.d(TAG, "UID " + u.getUid() + ": power=" + formatCharge(app.wifiPowerMah)); - } - } - - void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, - long rawUptimeUs, int statsType) { - final long totalRunningTimeMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType) - / 1000; - final double powerDrain = ((totalRunningTimeMs - mTotalAppWifiRunningTimeMs) * mWifiPowerOn) - / (1000*60*60); - app.wifiRunningTimeMs = totalRunningTimeMs; - app.wifiPowerMah = Math.max(0, powerDrain); - } - - @Override - public void reset() { - mTotalAppWifiRunningTimeMs = 0; - } -} diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java index d7b4d78c56cf..d49203c731e9 100644 --- a/core/java/com/android/internal/power/MeasuredEnergyStats.java +++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java @@ -193,34 +193,30 @@ public class MeasuredEnergyStats { return mAccumulatedEnergiesMicroJoules.length; } - // TODO: Get rid of the 'accumulate' boolean. It's always true. /** Updates the given standard energy bucket with the given energy if accumulate is true. */ - public void updateStandardBucket(@StandardEnergyBucket int bucket, long energyDeltaUJ, - boolean accumulate) { + public void updateStandardBucket(@StandardEnergyBucket int bucket, long energyDeltaUJ) { checkValidStandardBucket(bucket); - updateEntry(bucket, energyDeltaUJ, accumulate); + updateEntry(bucket, energyDeltaUJ); } /** Updates the given custom energy bucket with the given energy if accumulate is true. */ - public void updateCustomBucket(int customBucket, long energyDeltaUJ, boolean accumulate) { + public void updateCustomBucket(int customBucket, long energyDeltaUJ) { if (!isValidCustomBucket(customBucket)) { Slog.e(TAG, "Attempted to update invalid custom bucket " + customBucket); return; } final int index = customBucketToIndex(customBucket); - updateEntry(index, energyDeltaUJ, accumulate); + updateEntry(index, energyDeltaUJ); } /** Updates the given index with the given energy if accumulate is true. */ - private void updateEntry(int index, long energyDeltaUJ, boolean accumulate) { - if (accumulate) { - if (mAccumulatedEnergiesMicroJoules[index] >= 0L) { - mAccumulatedEnergiesMicroJoules[index] += energyDeltaUJ; - } else { - Slog.wtf(TAG, "Attempting to add " + energyDeltaUJ + " to unavailable bucket " - + getBucketName(index) + " whose value was " - + mAccumulatedEnergiesMicroJoules[index]); - } + private void updateEntry(int index, long energyDeltaUJ) { + if (mAccumulatedEnergiesMicroJoules[index] >= 0L) { + mAccumulatedEnergiesMicroJoules[index] += energyDeltaUJ; + } else { + Slog.wtf(TAG, "Attempting to add " + energyDeltaUJ + " to unavailable bucket " + + getBucketName(index) + " whose value was " + + mAccumulatedEnergiesMicroJoules[index]); } } 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/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl index f7d440d9fd95..95e0a3b524c5 100644 --- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -94,6 +94,6 @@ interface ITelephonyRegistry { void notifyBarringInfoChanged(int slotIndex, int subId, in BarringInfo barringInfo); void notifyPhysicalChannelConfigForSubscriber(in int subId, in List<PhysicalChannelConfig> configs); - void notifyDataEnabled(boolean enabled, int reason); + void notifyDataEnabled(in int phoneId, int subId, boolean enabled, int reason); void notifyAllowedNetworkTypesChanged(in int phoneId, in int subId, in Map allowedNetworkTypeList); } diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index ef2275d4218c..ab0149fce0a0 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -30,6 +30,7 @@ import android.view.IWindowSession; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.PointerIcon; +import android.view.ScrollCaptureResponse; import android.view.WindowInsets.Type.InsetsType; import android.window.ClientWindowFrames; @@ -160,7 +161,9 @@ public class BaseIWindow extends IWindow.Stub { @Override public void requestScrollCapture(IScrollCaptureCallbacks callbacks) { try { - callbacks.onUnavailable(); + callbacks.onScrollCaptureResponse( + new ScrollCaptureResponse.Builder().setDescription("Not Implemented").build()); + } catch (RemoteException ex) { // ignore } diff --git a/core/java/com/android/internal/view/OWNERS b/core/java/com/android/internal/view/OWNERS index 851d1f37522a..eb2478f6550a 100644 --- a/core/java/com/android/internal/view/OWNERS +++ b/core/java/com/android/internal/view/OWNERS @@ -18,3 +18,7 @@ per-file AppearanceRegion = file:/services/core/java/com/android/server/wm/OWNER per-file BaseIWIndow.java = file:/services/core/java/com/android/server/wm/OWNERS per-file RotationPolicy.java = file:/services/core/java/com/android/server/wm/OWNERS per-file WindowManagerPolicyThread.java = file:/services/core/java/com/android/server/wm/OWNERS + +# Scroll Capture +per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS +per-file *CaptureHelper*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS diff --git a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java index 85fa791b429c..a41511b74a7d 100644 --- a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java +++ b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java @@ -16,15 +16,19 @@ package com.android.internal.view; +import android.annotation.UiThread; +import android.content.Context; +import android.content.pm.ActivityInfo; import android.graphics.HardwareRenderer; import android.graphics.Matrix; import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.RenderNode; -import android.os.Handler; +import android.os.CancellationSignal; import android.util.DisplayMetrics; import android.util.Log; +import android.view.Display.ColorMode; import android.view.ScrollCaptureCallback; import android.view.ScrollCaptureSession; import android.view.Surface; @@ -44,32 +48,42 @@ import java.util.function.Consumer; * @param <V> the specific View subclass handled * @see ScrollCaptureViewHelper */ +@UiThread public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCallback { - public static final long NO_FRAME_PRODUCED = -1; - private static final String TAG = "ScrollCaptureViewSupport"; private static final boolean WAIT_FOR_ANIMATION = true; private final WeakReference<V> mWeakView; private final ScrollCaptureViewHelper<V> mViewHelper; - private ViewRenderer mRenderer; - private Handler mUiHandler; + private final ViewRenderer mRenderer; private boolean mStarted; private boolean mEnded; ScrollCaptureViewSupport(V containingView, ScrollCaptureViewHelper<V> viewHelper) { mWeakView = new WeakReference<>(containingView); mRenderer = new ViewRenderer(); - mUiHandler = containingView.getHandler(); + // TODO(b/177649144): provide access to color space from android.media.Image mViewHelper = viewHelper; } - // Base implementation of ScrollCaptureCallback + /** Based on ViewRootImpl#updateColorModeIfNeeded */ + @ColorMode + private static int getColorMode(View containingView) { + Context context = containingView.getContext(); + int colorMode = containingView.getViewRootImpl().mWindowAttributes.getColorMode(); + if (!context.getResources().getConfiguration().isScreenWideColorGamut()) { + colorMode = ActivityInfo.COLOR_MODE_DEFAULT; + } + return colorMode; + } @Override - public final void onScrollCaptureSearch(Consumer<Rect> onReady) { + public final void onScrollCaptureSearch(CancellationSignal signal, Consumer<Rect> onReady) { + if (signal.isCanceled()) { + return; + } V view = mWeakView.get(); mStarted = false; mEnded = false; @@ -82,7 +96,11 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa } @Override - public final void onScrollCaptureStart(ScrollCaptureSession session, Runnable onReady) { + public final void onScrollCaptureStart(ScrollCaptureSession session, CancellationSignal signal, + Runnable onReady) { + if (signal.isCanceled()) { + return; + } V view = mWeakView.get(); mEnded = false; @@ -99,18 +117,22 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa } @Override - public final void onScrollCaptureImageRequest(ScrollCaptureSession session, Rect requestRect) { + public final void onScrollCaptureImageRequest(ScrollCaptureSession session, + CancellationSignal signal, Rect requestRect, Consumer<Rect> onComplete) { + if (signal.isCanceled()) { + return; + } V view = mWeakView.get(); if (view == null || !view.isVisibleToUser()) { // Signal to the controller that we have a problem and can't continue. - session.notifyBufferSent(NO_FRAME_PRODUCED, new Rect()); + onComplete.accept(new Rect()); return; } // Ask the view to scroll as needed to bring this area into view. ScrollResult scrollResult = mViewHelper.onScrollRequested(view, session.getScrollBounds(), requestRect); if (scrollResult.availableArea.isEmpty()) { - session.notifyBufferSent(NO_FRAME_PRODUCED, scrollResult.availableArea); + onComplete.accept(scrollResult.availableArea); return; } view.invalidate(); // don't wait for vsync @@ -121,17 +143,13 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa viewCaptureArea.offset(0, -scrollResult.scrollDelta); if (WAIT_FOR_ANIMATION) { - Log.d(TAG, "render: delaying until animation"); view.postOnAnimation(() -> { - Log.d(TAG, "postOnAnimation(): rendering now"); - long resultFrame = mRenderer.renderView(view, viewCaptureArea); - Log.d(TAG, "notifyBufferSent: " + scrollResult.availableArea); - - session.notifyBufferSent(resultFrame, new Rect(scrollResult.availableArea)); + mRenderer.renderView(view, viewCaptureArea); + onComplete.accept(new Rect(scrollResult.availableArea)); }); } else { - long resultFrame = mRenderer.renderView(view, viewCaptureArea); - session.notifyBufferSent(resultFrame, new Rect(scrollResult.availableArea)); + mRenderer.renderView(view, viewCaptureArea); + onComplete.accept(new Rect(scrollResult.availableArea)); } } @@ -239,7 +257,7 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa mCaptureRenderNode.endRecording(); } - public long renderView(View view, Rect sourceRect) { + public void renderView(View view, Rect sourceRect) { if (updateForView(view)) { setupLighting(view); } @@ -258,7 +276,7 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa switch (request.syncAndDraw()) { case HardwareRenderer.SYNC_OK: case HardwareRenderer.SYNC_REDRAW_REQUESTED: - return frameNumber; + return; case HardwareRenderer.SYNC_FRAME_DROPPED: Log.e(TAG, "syncAndDraw(): SYNC_FRAME_DROPPED !"); @@ -270,7 +288,6 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa Log.e(TAG, "syncAndDraw(): SYNC_CONTEXT_IS_STOPPED !"); break; } - return NO_FRAME_PRODUCED; } public void trimMemory() { @@ -289,5 +306,17 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa mTempMatrix.mapRect(mTempRectF); mTempRectF.round(outRect); } + + public void setColorMode(@ColorMode int colorMode) { + mRenderer.setColorMode(colorMode); + } + } + + @Override + public String toString() { + return "ScrollCaptureViewSupport{" + + "view=" + mWeakView.get() + + ", helper=" + mViewHelper + + '}'; } } diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index 8982519eff96..b40ffb0136f2 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -24,6 +24,7 @@ import android.app.ActivityManager; import android.content.ComponentName; import android.content.pm.FeatureInfo; import android.content.pm.PackageManager; +import android.hardware.SensorPrivacyManager; import android.os.Build; import android.os.CarrierAssociatedAppEntry; import android.os.Environment; @@ -1234,10 +1235,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) { @@ -1252,6 +1253,14 @@ public class SystemConfig { addFeature(PackageManager.FEATURE_CROSS_LAYER_BLUR, 0); } + if (SensorPrivacyManager.USE_MICROPHONE_TOGGLE) { + addFeature(PackageManager.FEATURE_MICROPHONE_TOGGLE, 0); + } + + if (SensorPrivacyManager.USE_CAMERA_TOGGLE) { + addFeature(PackageManager.FEATURE_CAMERA_TOGGLE, 0); + } + for (String featureName : mUnavailableFeatures) { removeFeature(featureName); } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index dd1a5941eed8..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", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 1751be0af2cc..dc77bba44607 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -120,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); @@ -1488,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), 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/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto index d3c2d31779db..ec41a47a8798 100644 --- a/core/proto/android/server/activitymanagerservice.proto +++ b/core/proto/android/server/activitymanagerservice.proto @@ -627,6 +627,7 @@ message ActivityManagerServiceDumpProcessesProto { optional int64 duration_ms = 2; optional string tag = 3; optional int32 type = 4; + optional int32 reason_code = 5; } repeated PendingTempWhitelist pending_temp_whitelist = 26; diff --git a/core/proto/android/server/powerstatsservice.proto b/core/proto/android/server/powerstatsservice.proto index 4b1ee02a6c30..f64e146f87b7 100644 --- a/core/proto/android/server/powerstatsservice.proto +++ b/core/proto/android/server/powerstatsservice.proto @@ -144,7 +144,7 @@ message StateResidencyProto { */ optional int64 total_state_entry_count = 3; /** - * Last time this state was entered. Time in milliseconds since boot + * Last time this state was entered. Walltime in milliseconds since Unix epoch. */ optional int64 last_entry_timestamp_ms = 4; } @@ -208,7 +208,7 @@ message EnergyConsumerResultProto { /** Unique index identifying the energy consumer. */ optional int32 id = 1; - /** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */ + /** Walltime in milliseconds since Unix epoch */ optional int64 timestamp_ms = 2; /** Accumulated energy since device boot in microwatt-seconds (uWs) */ @@ -247,7 +247,7 @@ message EnergyMeasurementProto { */ optional int32 id = 1; - /** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */ + /** Walltime in milliseconds since Unix epoch */ optional int64 timestamp_ms = 2; /** Accumulated energy since device boot in microwatt-seconds (uWs) */ diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index a1be865c0c16..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]; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 5dd85805cfc1..2150efef61e8 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 --> @@ -3972,17 +3982,6 @@ <permission android:name="com.android.permission.INSTALL_EXISTING_PACKAGES" android:protectionLevel="signature|privileged" /> - <!-- Allows an application to use the package installer v2 APIs. - <p>The package installer v2 APIs are still a work in progress and we're - currently validating they work in all scenarios. - <p>Not for use by third-party applications. - TODO(b/152310230): use this permission to protect only Incremental installations - once the APIs are confirmed to be sufficient. - @hide - --> - <permission android:name="com.android.permission.USE_INSTALLER_V2" - android:protectionLevel="signature|verifier" /> - <!-- Allows an application to use System Data Loaders. <p>Not for use by third-party applications. @hide @@ -5421,6 +5420,12 @@ @hide --> <permission android:name="android.permission.MANAGE_SENSOR_PRIVACY" android:protectionLevel="signature" /> + + <!-- @SystemApi Allows sensor privacy changes to be observed. + @hide --> + <permission android:name="android.permission.OBSERVE_SENSOR_PRIVACY" + android:protectionLevel="signature|installer" /> + <!-- @SystemApi Permission that protects the {@link Intent#ACTION_REVIEW_ACCESSIBILITY_SERVICES} intent. @hide --> diff --git a/core/res/res/layout/splash_screen_view.xml b/core/res/res/layout/splash_screen_view.xml index 513da5e431e5..e6d724f1ecb2 100644 --- a/core/res/res/layout/splash_screen_view.xml +++ b/core/res/res/layout/splash_screen_view.xml @@ -21,14 +21,16 @@ android:orientation="vertical"> <View android:id="@+id/splashscreen_icon_view" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:layout_gravity="center"/> + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:layout_gravity="center" + android:contentDescription="@string/splash_screen_view_icon_description"/> <View android:id="@+id/splashscreen_branding_view" android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_gravity="center_horizontal|bottom" - android:layout_marginBottom="60dp"/> + android:layout_marginBottom="60dp" + android:contentDescription="@string/splash_screen_view_branding_description"/> </android.window.SplashScreenView>
\ No newline at end of file diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml index 29f2b6f14b57..4410e944db39 100644 --- a/core/res/res/values-night/colors.xml +++ b/core/res/res/values-night/colors.xml @@ -26,6 +26,10 @@ <color name="notification_default_color_dark">#ddffffff</color> + <color name="notification_primary_text_color_current">@color/notification_primary_text_color_dark</color> + <color name="notification_secondary_text_color_current">@color/notification_secondary_text_color_dark</color> + <color name="notification_default_color_current">@color/notification_default_color_dark</color> + <color name="chooser_row_divider">@color/list_divider_color_dark</color> <color name="chooser_gradient_background">@color/loading_gradient_background_color_dark</color> <color name="chooser_gradient_highlight">@color/loading_gradient_highlight_color_dark</color> diff --git a/core/res/res/values-night/values.xml b/core/res/res/values-night/values.xml index 952cdd08451c..3fd82b874a84 100644 --- a/core/res/res/values-night/values.xml +++ b/core/res/res/values-night/values.xml @@ -28,9 +28,4 @@ </style> <style name="Theme.DeviceDefault.QuickSettings.Dialog" parent="Theme.DeviceDefault.Dialog" /> - - <style name="TextAppearance.Material.Notification"> - <item name="textColor">?attr/textColorPrimary</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_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/colors.xml b/core/res/res/values/colors.xml index 59c260cecfbf..d79c01faaa36 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -143,6 +143,10 @@ <color name="notification_default_color_dark">@color/primary_text_default_material_light</color> <color name="notification_default_color_light">#a3202124</color> + <color name="notification_primary_text_color_current">@color/notification_primary_text_color_light</color> + <color name="notification_secondary_text_color_current">@color/notification_secondary_text_color_light</color> + <color name="notification_default_color_current">@color/notification_default_color_light</color> + <color name="notification_default_color">#757575</color> <!-- Gray 600 --> <color name="notification_action_button_text_color">@color/notification_default_color</color> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 592a3a1ff083..12cb3980f785 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1953,6 +1953,8 @@ <string name="config_systemShell" translatable="false">com.android.shell</string> <!-- The name of the package that will hold the system contacts role. --> <string name="config_systemContacts" translatable="false">com.android.contacts</string> + <!-- The name of the package that will hold the speech recognizer role by default. --> + <string name="config_systemSpeechRecognizer" translatable="false"></string> <!-- The name of the package that will be allowed to change its components' label/icon. --> <string name="config_overrideComponentUiPackage" translatable="false"></string> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 4732e5fbf84f..2004d0a8d15c 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3084,6 +3084,7 @@ <public name="hand_minuteTintMode"/> <public name="hand_secondTint"/> <public name="hand_secondTintMode"/> + <public name="dataExtractionRules"/> </public-group> <public-group type="drawable" first-id="0x010800b5"> @@ -3165,6 +3166,8 @@ <public name="config_customMediaKeyDispatcher" /> <!-- @hide @SystemApi --> <public name="config_customMediaSessionPolicyProvider" /> + <!-- @hide @SystemApi --> + <public name="config_systemSpeechRecognizer" /> </public-group> <public-group type="id" first-id="0x01020055"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 3505dee134c0..576c44d6d201 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1644,7 +1644,7 @@ <!-- Message shown during acqusition when the user's face is turned too far left or right [CHAR LIMIT=50] --> <string name="face_acquired_pan_too_extreme">Turn your head a little less.</string> <!-- Message shown during acqusition when the user's face is tilted too high or too low [CHAR LIMIT=50] --> - <string name="face_acquired_tilt_too_extreme">Turn your head a little less.</string> + <string name="face_acquired_tilt_too_extreme">Tilt your head a little less.</string> <!-- Message shown during acquisiton when the user's face is tilted too far left or right [CHAR LIMIT=50] --> <string name="face_acquired_roll_too_extreme">Turn your head a little less.</string> <!-- Message shown during acquisition when the user's face is obscured [CHAR LIMIT=50] --> @@ -5849,4 +5849,8 @@ ul.</string> <!--- Label for notification channel for all sensor privacy related notifications. [CHAR LIMIT=NONE] --> <string name="sensor_privacy_notification_channel_label">Sensor Privacy</string> + <!-- Content description for the icon on the splash screen. [CHAR LIMIT=50] --> + <string name="splash_screen_view_icon_description">Application icon</string> + <!-- Content description for the branding image on the splash screen. [CHAR LIMIT=50] --> + <string name="splash_screen_view_branding_description">Application branding image</string> </resources> diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml index 158289a39d5c..3c4a5d4aab73 100644 --- a/core/res/res/values/styles_material.xml +++ b/core/res/res/values/styles_material.xml @@ -466,18 +466,20 @@ please see styles_device_defaults.xml. </style> <style name="TextAppearance.Material.Notification"> - <item name="textColor">@color/notification_secondary_text_color_light</item> + <item name="textColor">@color/notification_secondary_text_color_current</item> <item name="textSize">@dimen/notification_text_size</item> </style> <style name="TextAppearance.Material.Notification.Reply" /> <style name="TextAppearance.Material.Notification.Title"> + <item name="textColor">@color/notification_primary_text_color_current</item> <item name="fontFamily">sans-serif-medium</item> <item name="textSize">@dimen/notification_title_text_size</item> </style> <style name="TextAppearance.Material.Notification.BigTitle"> + <item name="textColor">@color/notification_primary_text_color_current</item> <item name="fontFamily">sans-serif-medium</item> <item name="textSize">@dimen/notification_big_title_text_size</item> </style> @@ -492,9 +494,8 @@ please see styles_device_defaults.xml. <style name="TextAppearance.Material.Notification.Time" parent="TextAppearance.Material.Notification.Info" /> - <style name="TextAppearance.Material.Notification.Emphasis"> - <item name="textColor">#66000000</item> - </style> + <!-- unused; keep identical to parent --> + <style name="TextAppearance.Material.Notification.Emphasis"/> <style name="TextAppearance.Material.Notification.Conversation.AppName" parent="TextAppearance.Material.Notification.Title"> <item name="android:textSize">16sp</item> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 29b8e6e08947..7ad05de1bdd0 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4204,4 +4204,6 @@ <java-symbol type="bool" name="config_telephony5gNonStandalone" /> <java-symbol type="bool" name="config_voice_data_sms_auto_fallback" /> + + <java-symbol type="bool" name="config_enableOneHandedKeyguard" /> </resources> diff --git a/core/tests/benchmarks/src/android/os/ParcelableBenchmark.java b/core/tests/benchmarks/src/android/os/ParcelableBenchmark.java new file mode 100644 index 000000000000..1cf430205627 --- /dev/null +++ b/core/tests/benchmarks/src/android/os/ParcelableBenchmark.java @@ -0,0 +1,133 @@ +/* + * 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.os; + +import android.annotation.SuppressLint; +import android.graphics.Point; +import android.graphics.Rect; +import android.util.MergedConfiguration; +import android.view.InsetsSource; +import android.view.InsetsState; + +import com.google.caliper.AfterExperiment; +import com.google.caliper.BeforeExperiment; + +/** + * Benchmark of read/write large Parcelable class. This also shows the performance of different + * implementations for nested Parcelable class: + * <ul> + * <li>Well-written read/writeFromParcel (direct access)</li> + * <li>read/writeTypedObject (object creation + addition int to indicate nullity)</li> + * <li>read/writeParcelable (object creation + addition type String)</li> + * </ul> + */ +public class ParcelableBenchmark { + private Parcel mParcel; + + @BeforeExperiment + protected void setUp() { + mParcel = Parcel.obtain(); + } + + @AfterExperiment + protected void tearDown() { + mParcel.recycle(); + mParcel = null; + } + + public void timeReadWriteMergedConfiguration(int reps) { + final MergedConfiguration mergedConfiguration = new MergedConfiguration(); + for (int i = 0; i < reps; i++) { + mergedConfiguration.writeToParcel(mParcel, 0); + mParcel.setDataPosition(0); + mergedConfiguration.readFromParcel(mParcel); + } + } + + public void timeReadWriteInsetsState(int reps) { + final InsetsState insetsState = new InsetsState(); + for (int i = 0; i < InsetsState.SIZE; i++) { + insetsState.addSource(new InsetsSource(i)); + } + for (int i = 0; i < reps; i++) { + insetsState.writeToParcel(mParcel, 0); + mParcel.setDataPosition(0); + insetsState.readFromParcel(mParcel); + } + } + + public void timeReadWritePointArray(int reps) { + final PointArray pointArray = new PointArray(); + for (int i = 0; i < reps; i++) { + pointArray.writeToParcel(mParcel, 0); + mParcel.setDataPosition(0); + pointArray.readFromParcel(mParcel); + } + } + + public void timeReadWritePointArrayFast(int reps) { + final PointArrayFast pointArray = new PointArrayFast(); + for (int i = 0; i < reps; i++) { + pointArray.writeToParcel(mParcel, 0); + mParcel.setDataPosition(0); + pointArray.readFromParcel(mParcel); + } + } + + @SuppressLint("ParcelCreator") + private static class PointArray implements Parcelable { + Rect mBounds = new Rect(); + Point[] mPoints = new Point[10]; + { + for (int i = 0; i < mPoints.length; i++) { + mPoints[i] = new Point(); + } + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(mBounds, flags); + dest.writeParcelableArray(mPoints, flags); + } + + void readFromParcel(Parcel in) { + mBounds = in.readParcelable(Rect.class.getClassLoader()); + mPoints = in.readParcelableArray(Point.class.getClassLoader(), Point.class); + } + + @Override + public int describeContents() { + return 0; + } + } + + @SuppressLint("ParcelCreator") + private static class PointArrayFast extends PointArray { + + @Override + public void writeToParcel(Parcel dest, int flags) { + mBounds.writeToParcel(dest, flags); + dest.writeTypedArray(mPoints, flags); + } + + @Override + void readFromParcel(Parcel in) { + mBounds.readFromParcel(in); + in.readTypedArray(mPoints, Point.CREATOR); + } + } +} diff --git a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java index 1573c19b89a3..7cb680499d98 100644 --- a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java +++ b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java @@ -37,6 +37,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Parcel; +import android.os.UserHandle; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -124,13 +125,13 @@ public class PeopleSpaceTileTest { } @Test - public void testUid() { + public void testUserHandle() { PeopleSpaceTile tile = new PeopleSpaceTile .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps) - .setUid(42) + .setUserHandle(new UserHandle(0)) .build(); - assertThat(tile.getUid()).isEqualTo(42); + assertThat(tile.getUserHandle()).isEqualTo(new UserHandle(0)); } @Test @@ -227,7 +228,7 @@ public class PeopleSpaceTileTest { .setUserName("name") .setUserIcon(mIcon) .setContactUri(Uri.parse("contact")) - .setUid(42) + .setUserHandle(new UserHandle(1)) .setPackageName("package.name") .setLastInteractionTimestamp(7L) .setIsImportantConversation(true) @@ -246,7 +247,7 @@ public class PeopleSpaceTileTest { assertThat(readTile.getUserName()).isEqualTo(tile.getUserName()); assertThat(readTile.getUserIcon().toString()).isEqualTo(tile.getUserIcon().toString()); assertThat(readTile.getContactUri()).isEqualTo(tile.getContactUri()); - assertThat(readTile.getUid()).isEqualTo(tile.getUid()); + assertThat(readTile.getUserHandle()).isEqualTo(tile.getUserHandle()); assertThat(readTile.getPackageName()).isEqualTo(tile.getPackageName()); assertThat(readTile.getLastInteractionTimestamp()).isEqualTo( tile.getLastInteractionTimestamp()); 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 80165f065995..fa1aa5eab26c 100644 --- a/core/tests/coretests/src/android/view/OWNERS +++ b/core/tests/coretests/src/android/view/OWNERS @@ -9,3 +9,6 @@ per-file *Focus* = file:/services/core/java/com/android/server/wm/OWNERS per-file *Insets* = file:/services/core/java/com/android/server/wm/OWNERS per-file *View* = file:/services/core/java/com/android/server/wm/OWNERS per-file *Visibility* = file:/services/core/java/com/android/server/wm/OWNERS + +# Scroll Capture +per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS diff --git a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java index b9cf1e4a234c..516fb76eeaf7 100644 --- a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java +++ b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java @@ -16,16 +16,12 @@ package android.view; -import static androidx.test.InstrumentationRegistry.getInstrumentation; import static androidx.test.InstrumentationRegistry.getTargetContext; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -35,6 +31,7 @@ import static org.mockito.Mockito.when; import android.graphics.Point; import android.graphics.Rect; import android.os.Handler; +import android.os.ICancellationSignal; import androidx.test.runner.AndroidJUnit4; @@ -42,9 +39,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import org.mockito.stubbing.Answer; /** * Tests of {@link ScrollCaptureConnection}. @@ -56,261 +51,138 @@ public class ScrollCaptureConnectionTest { private final Point mPositionInWindow = new Point(1, 2); private final Rect mLocalVisibleRect = new Rect(2, 3, 4, 5); private final Rect mScrollBounds = new Rect(3, 4, 5, 6); + private final TestScrollCaptureCallback mCallback = new TestScrollCaptureCallback(); + + private ScrollCaptureTarget mTarget; + private ScrollCaptureConnection mConnection; private Handler mHandler; - private ScrollCaptureTarget mTarget1; @Mock private Surface mSurface; @Mock - private IScrollCaptureCallbacks mConnectionCallbacks; - @Mock - private View mMockView1; + private IScrollCaptureCallbacks mRemote; @Mock - private ScrollCaptureCallback mCallback1; + private View mView; @Before public void setUp() { MockitoAnnotations.initMocks(this); mHandler = new Handler(getTargetContext().getMainLooper()); + when(mSurface.isValid()).thenReturn(true); + when(mView.getScrollCaptureHint()).thenReturn(View.SCROLL_CAPTURE_HINT_INCLUDE); - when(mMockView1.getHandler()).thenReturn(mHandler); - when(mMockView1.getScrollCaptureHint()).thenReturn(View.SCROLL_CAPTURE_HINT_INCLUDE); - - mTarget1 = new ScrollCaptureTarget( - mMockView1, mLocalVisibleRect, mPositionInWindow, mCallback1); - mTarget1.setScrollBounds(mScrollBounds); - } - - /** Test the DelayedAction timeout helper class works as expected. */ - @Test - public void testDelayedAction() { - Runnable action = Mockito.mock(Runnable.class); - ScrollCaptureConnection.DelayedAction delayed = - new ScrollCaptureConnection.DelayedAction(mHandler, 100, action); - try { - Thread.sleep(200); - } catch (InterruptedException ex) { - /* ignore */ - } - getInstrumentation().waitForIdleSync(); - assertFalse(delayed.cancel()); - assertFalse(delayed.timeoutNow()); - verify(action, times(1)).run(); - } - - /** Test the DelayedAction cancel() */ - @Test - public void testDelayedAction_cancel() { - Runnable action = Mockito.mock(Runnable.class); - ScrollCaptureConnection.DelayedAction delayed = - new ScrollCaptureConnection.DelayedAction(mHandler, 100, action); - try { - Thread.sleep(50); - } catch (InterruptedException ex) { - /* ignore */ - } - assertTrue(delayed.cancel()); - assertFalse(delayed.timeoutNow()); - try { - Thread.sleep(200); - } catch (InterruptedException ex) { - /* ignore */ - } - getInstrumentation().waitForIdleSync(); - verify(action, never()).run(); - } - - /** Test the DelayedAction timeoutNow() - for testing only */ - @Test - public void testDelayedAction_timeoutNow() { - Runnable action = Mockito.mock(Runnable.class); - ScrollCaptureConnection.DelayedAction delayed = - new ScrollCaptureConnection.DelayedAction(mHandler, 100, action); - try { - Thread.sleep(50); - } catch (InterruptedException ex) { - /* ignore */ - } - assertTrue(delayed.timeoutNow()); - assertFalse(delayed.cancel()); - getInstrumentation().waitForIdleSync(); - verify(action, times(1)).run(); + mTarget = new ScrollCaptureTarget(mView, mLocalVisibleRect, mPositionInWindow, mCallback); + mTarget.setScrollBounds(mScrollBounds); + mConnection = new ScrollCaptureConnection(Runnable::run, mTarget, mRemote); } /** Test creating a client with valid info */ @Test public void testConstruction() { - new ScrollCaptureConnection(mTarget1, mConnectionCallbacks); + ScrollCaptureTarget target = new ScrollCaptureTarget( + mView, mLocalVisibleRect, mPositionInWindow, mCallback); + target.setScrollBounds(new Rect(1, 2, 3, 4)); + new ScrollCaptureConnection(Runnable::run, target, mRemote); } /** Test creating a client fails if arguments are not valid. */ @Test public void testConstruction_requiresScrollBounds() { try { - mTarget1.setScrollBounds(null); - new ScrollCaptureConnection(mTarget1, mConnectionCallbacks); + mTarget.setScrollBounds(null); + new ScrollCaptureConnection(Runnable::run, mTarget, mRemote); fail("An exception was expected."); } catch (RuntimeException ex) { // Ignore, expected. } } - @SuppressWarnings("SameParameterValue") - private static Answer<Void> runRunnable(int arg) { - return invocation -> { - Runnable r = invocation.getArgument(arg); - r.run(); - return null; - }; - } - - @SuppressWarnings("SameParameterValue") - private static Answer<Void> reportBufferSent(int sessionArg, long frameNum, Rect capturedArea) { - return invocation -> { - ScrollCaptureSession session = invocation.getArgument(sessionArg); - session.notifyBufferSent(frameNum, capturedArea); - return null; - }; - } - /** @see ScrollCaptureConnection#startCapture(Surface) */ @Test public void testStartCapture() throws Exception { - final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1, - mConnectionCallbacks); - - // Have the session start accepted immediately - doAnswer(runRunnable(1)).when(mCallback1) - .onScrollCaptureStart(any(ScrollCaptureSession.class), any(Runnable.class)); - connection.startCapture(mSurface); - getInstrumentation().waitForIdleSync(); - - verify(mCallback1, times(1)) - .onScrollCaptureStart(any(ScrollCaptureSession.class), any(Runnable.class)); - verify(mConnectionCallbacks, times(1)).onCaptureStarted(); - verifyNoMoreInteractions(mConnectionCallbacks); + mConnection.startCapture(mSurface); + + mCallback.completeStartRequest(); + assertTrue(mConnection.isStarted()); + + verify(mRemote, times(1)).onCaptureStarted(); + verifyNoMoreInteractions(mRemote); } @Test - public void testStartCaptureTimeout() throws Exception { - final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1, - mConnectionCallbacks); - connection.startCapture(mSurface); + public void testStartCapture_cancellation() throws Exception { + ICancellationSignal signal = mConnection.startCapture(mSurface); + signal.cancel(); - // Force timeout to fire - connection.getTimeoutAction().timeoutNow(); + mCallback.completeStartRequest(); + assertFalse(mConnection.isStarted()); - getInstrumentation().waitForIdleSync(); - verify(mCallback1, times(1)).onScrollCaptureEnd(any(Runnable.class)); - } - - private void startCapture(ScrollCaptureConnection connection) throws Exception { - doAnswer(runRunnable(1)).when(mCallback1) - .onScrollCaptureStart(any(ScrollCaptureSession.class), any(Runnable.class)); - connection.startCapture(mSurface); - getInstrumentation().waitForIdleSync(); - reset(mCallback1, mConnectionCallbacks); + verifyNoMoreInteractions(mRemote); } /** @see ScrollCaptureConnection#requestImage(Rect) */ @Test public void testRequestImage() throws Exception { - final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1, - mConnectionCallbacks); - startCapture(connection); - - // Stub the callback to complete the request immediately - doAnswer(reportBufferSent(/* sessionArg */ 0, /* frameNum */ 1L, new Rect(1, 2, 3, 4))) - .when(mCallback1) - .onScrollCaptureImageRequest(any(ScrollCaptureSession.class), any(Rect.class)); - - // Make the inbound binder call - connection.requestImage(new Rect(1, 2, 3, 4)); + mConnection.startCapture(mSurface); + mCallback.completeStartRequest(); + reset(mRemote); - // Wait for handler thread dispatch - getInstrumentation().waitForIdleSync(); - verify(mCallback1, times(1)).onScrollCaptureImageRequest( - any(ScrollCaptureSession.class), eq(new Rect(1, 2, 3, 4))); + mConnection.requestImage(new Rect(1, 2, 3, 4)); + mCallback.completeImageRequest(new Rect(1, 2, 3, 4)); - // Wait for binder thread dispatch - getInstrumentation().waitForIdleSync(); - verify(mConnectionCallbacks, times(1)) - .onCaptureBufferSent(eq(1L), eq(new Rect(1, 2, 3, 4))); - - verifyNoMoreInteractions(mCallback1, mConnectionCallbacks); + verify(mRemote, times(1)) + .onImageRequestCompleted(eq(0), eq(new Rect(1, 2, 3, 4))); + verifyNoMoreInteractions(mRemote); } @Test - public void testRequestImageTimeout() throws Exception { - final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1, - mConnectionCallbacks); - startCapture(connection); - - // Make the inbound binder call - connection.requestImage(new Rect(1, 2, 3, 4)); - - // Wait for handler thread dispatch - getInstrumentation().waitForIdleSync(); - verify(mCallback1, times(1)).onScrollCaptureImageRequest( - any(ScrollCaptureSession.class), eq(new Rect(1, 2, 3, 4))); - - // Force timeout to fire - connection.getTimeoutAction().timeoutNow(); - getInstrumentation().waitForIdleSync(); - - // (callback not stubbed, does nothing) - // Timeout triggers request to end capture - verify(mCallback1, times(1)).onScrollCaptureEnd(any(Runnable.class)); - verifyNoMoreInteractions(mCallback1, mConnectionCallbacks); + public void testRequestImage_cancellation() throws Exception { + mConnection.startCapture(mSurface); + mCallback.completeStartRequest(); + reset(mRemote); + + ICancellationSignal signal = mConnection.requestImage(new Rect(1, 2, 3, 4)); + signal.cancel(); + mCallback.completeImageRequest(new Rect(1, 2, 3, 4)); + + verifyNoMoreInteractions(mRemote); } /** @see ScrollCaptureConnection#endCapture() */ @Test public void testEndCapture() throws Exception { - final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1, - mConnectionCallbacks); - startCapture(connection); - - // Stub the callback to complete the request immediately - doAnswer(runRunnable(0)) - .when(mCallback1) - .onScrollCaptureEnd(any(Runnable.class)); - - // Make the inbound binder call - connection.endCapture(); + mConnection.startCapture(mSurface); + mCallback.completeStartRequest(); + reset(mRemote); - // Wait for handler thread dispatch - getInstrumentation().waitForIdleSync(); - verify(mCallback1, times(1)).onScrollCaptureEnd(any(Runnable.class)); + mConnection.endCapture(); + mCallback.completeEndRequest(); - // Wait for binder thread dispatch - getInstrumentation().waitForIdleSync(); - verify(mConnectionCallbacks, times(1)).onConnectionClosed(); - - verifyNoMoreInteractions(mCallback1, mConnectionCallbacks); + // And the reply is sent + verify(mRemote, times(1)).onCaptureEnded(); + verifyNoMoreInteractions(mRemote); } + /** @see ScrollCaptureConnection#endCapture() */ @Test - public void testEndCaptureTimeout() throws Exception { - final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1, - mConnectionCallbacks); - startCapture(connection); + public void testEndCapture_cancellation() throws Exception { + mConnection.startCapture(mSurface); + mCallback.completeStartRequest(); + reset(mRemote); - // Make the inbound binder call - connection.endCapture(); + ICancellationSignal signal = mConnection.endCapture(); + signal.cancel(); + mCallback.completeEndRequest(); - // Wait for handler thread dispatch - getInstrumentation().waitForIdleSync(); - verify(mCallback1, times(1)).onScrollCaptureEnd(any(Runnable.class)); - - // Force timeout to fire - connection.getTimeoutAction().timeoutNow(); - - // Wait for binder thread dispatch - getInstrumentation().waitForIdleSync(); - verify(mConnectionCallbacks, times(1)).onConnectionClosed(); + verifyNoMoreInteractions(mRemote); + } - verifyNoMoreInteractions(mCallback1, mConnectionCallbacks); + @Test + public void testClose() throws Exception { + mConnection.close(); + assertFalse(mConnection.isConnected()); + verifyNoMoreInteractions(mRemote); } + } diff --git a/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java b/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java new file mode 100644 index 000000000000..cc229e11dcf2 --- /dev/null +++ b/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java @@ -0,0 +1,415 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import static androidx.test.InstrumentationRegistry.getTargetContext; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.annotation.Nullable; +import android.content.Context; +import android.graphics.Point; +import android.graphics.Rect; +import android.os.CancellationSignal; +import android.os.SystemClock; + +import androidx.annotation.NonNull; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +/** + * Tests of {@link ScrollCaptureTargetSelector}. + */ +@RunWith(AndroidJUnit4.class) +public class ScrollCaptureSearchResultsTest { + + + private static final Rect EMPTY_RECT = new Rect(); + private static final String TAG = "Test"; + + private final Executor mDirectExec = Runnable::run; + private Executor mBgExec; + + @Before + public void setUp() { + mBgExec = Executors.newSingleThreadExecutor(); + } + + @Test + public void testNoTargets() { + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec); + assertTrue(results.isComplete()); + + assertNull("Expected null due to empty queue", results.getTopResult()); + } + + @Test + public void testNoValidTargets() { + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec); + + FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(mDirectExec); + callback1.setScrollBounds(EMPTY_RECT); + ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50), + new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); + + // Supplies scrollBounds = empty rect + FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(mDirectExec); + callback2.setScrollBounds(EMPTY_RECT); + ScrollCaptureTarget target2 = createTarget(callback2, new Rect(20, 30, 40, 50), + new Point(0, 20), View.SCROLL_CAPTURE_HINT_INCLUDE); + + results.addTarget(target1); + results.addTarget(target2); + + assertTrue(results.isComplete()); + assertNull("Expected null due to no valid targets", results.getTopResult()); + } + + @Test + public void testSingleTarget() { + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec); + FakeScrollCaptureCallback callback = new FakeScrollCaptureCallback(mDirectExec); + ScrollCaptureTarget target = createTarget(callback, + new Rect(20, 30, 40, 50), new Point(10, 10), + View.SCROLL_CAPTURE_HINT_AUTO); + callback.setScrollBounds(new Rect(2, 2, 18, 18)); + + results.addTarget(target); + assertTrue(results.isComplete()); + + ScrollCaptureTarget result = results.getTopResult(); + assertSame("Excepted the same target as a result", target, result); + assertEquals("result has wrong scroll bounds", + new Rect(2, 2, 18, 18), result.getScrollBounds()); + } + + @Test + public void testSingleTarget_backgroundThread() throws InterruptedException { + FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(mBgExec); + ScrollCaptureTarget target1 = createTarget(callback1, + new Rect(20, 30, 40, 50), new Point(10, 10), + View.SCROLL_CAPTURE_HINT_AUTO); + callback1.setDelay(100); + callback1.setScrollBounds(new Rect(2, 2, 18, 18)); + + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec); + results.addTarget(target1); + + CountDownLatch latch = new CountDownLatch(1); + results.setOnCompleteListener(latch::countDown); + if (!latch.await(200, TimeUnit.MILLISECONDS)) { + fail("onComplete listener was expected"); + } + + ScrollCaptureTarget result = results.getTopResult(); + assertSame("Excepted the single target1 as a result", target1, result); + assertEquals("Result has wrong scroll bounds", + new Rect(2, 2, 18, 18), result.getScrollBounds()); + } + + @Test + public void testRanking() { + + // 1 - Empty + FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(mDirectExec); + callback1.setScrollBounds(EMPTY_RECT); + ViewGroup targetView1 = new FakeView(getTargetContext(), 0, 0, 60, 60, 1); + ScrollCaptureTarget target1 = createTargetWithView(targetView1, callback1, + new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); + + // 2 - 10x10 + HINT_INCLUDE + FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(mDirectExec); + callback2.setScrollBounds(new Rect(0, 0, 10, 10)); + ViewGroup targetView2 = new FakeView(getTargetContext(), 0, 0, 60, 60, 2); + ScrollCaptureTarget target2 = createTargetWithView(targetView2, callback2, + new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_INCLUDE); + + // 3 - 20x20 + AUTO + FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback(mDirectExec); + callback3.setScrollBounds(new Rect(0, 0, 20, 20)); + ViewGroup targetView3 = new FakeView(getTargetContext(), 0, 0, 60, 60, 3); + ScrollCaptureTarget target3 = createTargetWithView(targetView3, callback3, + new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); + + // 4 - 30x30 + AUTO + FakeScrollCaptureCallback callback4 = new FakeScrollCaptureCallback(mDirectExec); + callback4.setScrollBounds(new Rect(0, 0, 10, 10)); + ViewGroup targetView4 = new FakeView(getTargetContext(), 0, 0, 60, 60, 4); + ScrollCaptureTarget target4 = createTargetWithView(targetView4, callback4, + new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); + + // 5 - 10x10 + child of #4 + FakeScrollCaptureCallback callback5 = new FakeScrollCaptureCallback(mDirectExec); + callback5.setScrollBounds(new Rect(0, 0, 10, 10)); + ViewGroup targetView5 = new FakeView(getTargetContext(), 0, 0, 60, 60, 5); + ScrollCaptureTarget target5 = createTargetWithView(targetView5, callback5, + new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); + targetView4.addView(targetView5); + + // 6 - 20x20 + child of #4 + FakeScrollCaptureCallback callback6 = new FakeScrollCaptureCallback(mDirectExec); + callback6.setScrollBounds(new Rect(0, 0, 20, 20)); + ViewGroup targetView6 = new FakeView(getTargetContext(), 0, 0, 60, 60, 6); + ScrollCaptureTarget target6 = createTargetWithView(targetView6, callback6, + new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); + targetView4.addView(targetView6); + + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec); + results.addTarget(target1); + results.addTarget(target2); + results.addTarget(target3); + results.addTarget(target4); + results.addTarget(target5); + results.addTarget(target6); + assertTrue(results.isComplete()); + + // Verify "top" result + assertEquals(target2, results.getTopResult()); + + // Verify priority ("best" first) + assertThat(results.getTargets()) + .containsExactly( + target2, + target6, + target5, + target4, + target3, + target1); + } + + /** + * If a timeout expires, late results are ignored. + */ + @Test + public void testTimeout() { + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec); + + // callback 1, 10x10, hint=AUTO, responds after 100ms from bg thread + FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(mBgExec); + callback1.setScrollBounds(new Rect(5, 5, 15, 15)); + callback1.setDelay(100); + ScrollCaptureTarget target1 = createTarget( + callback1, new Rect(20, 30, 40, 50), new Point(10, 10), + View.SCROLL_CAPTURE_HINT_AUTO); + results.addTarget(target1); + + // callback 2, 20x20, hint=AUTO, responds after 5s from bg thread + FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(mBgExec); + callback2.setScrollBounds(new Rect(0, 0, 20, 20)); + callback2.setDelay(1000); + ScrollCaptureTarget target2 = createTarget( + callback2, new Rect(20, 30, 40, 50), new Point(10, 10), + View.SCROLL_CAPTURE_HINT_AUTO); + results.addTarget(target2); + + // callback 3, 20x20, hint=INCLUDE, responds after 10s from bg thread + FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback(mBgExec); + callback3.setScrollBounds(new Rect(0, 0, 20, 20)); + callback3.setDelay(1500); + ScrollCaptureTarget target3 = createTarget( + callback3, new Rect(20, 30, 40, 50), new Point(10, 10), + View.SCROLL_CAPTURE_HINT_INCLUDE); + results.addTarget(target3); + + // callback 1 will be received + // callback 2 & 3 will be ignored due to timeout + SystemClock.sleep(500); + results.finish(); + + ScrollCaptureTarget result = results.getTopResult(); + assertSame("Expected target1 as the result, due to timeouts of others", target1, result); + assertEquals("callback1 should have been called", + 1, callback1.getOnScrollCaptureSearchCount()); + assertEquals("callback2 should have been called", + 1, callback2.getOnScrollCaptureSearchCount()); + assertEquals("callback3 should have been called", + 1, callback3.getOnScrollCaptureSearchCount()); + + assertEquals("result has wrong scroll bounds", + new Rect(5, 5, 15, 15), result.getScrollBounds()); + assertNull("target2 should not have been updated", + target2.getScrollBounds()); + assertNull("target3 should not have been updated", + target3.getScrollBounds()); + } + + @Test + public void testWithCallbackMultipleReplies() { + // Calls response methods 3 times each + ScrollCaptureCallback callback1 = new CallbackStub() { + @Override + public void onScrollCaptureSearch(@NonNull CancellationSignal signal, + @NonNull Consumer<Rect> onReady) { + onReady.accept(new Rect(1, 2, 3, 4)); + onReady.accept(new Rect(9, 10, 11, 12)); + } + }; + + ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50), + new Point(10, 10), View.SCROLL_CAPTURE_HINT_AUTO); + + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec); + results.addTarget(target1); + assertTrue(results.isComplete()); + + ScrollCaptureTarget result = results.getTopResult(); + assertSame("Expected target1", target1, result); + assertEquals("result has wrong scroll bounds", + new Rect(1, 2, 3, 4), result.getScrollBounds()); + } + + private void setupTargetView(View view, Rect localVisibleRect, int scrollCaptureHint) { + view.setScrollCaptureHint(scrollCaptureHint); + view.onVisibilityAggregated(true); + // Treat any offset as padding, outset localVisibleRect on all sides and use this as + // child bounds + Rect bounds = new Rect(localVisibleRect); + bounds.inset(-bounds.left, -bounds.top, bounds.left, bounds.top); + view.layout(bounds.left, bounds.top, bounds.right, bounds.bottom); + view.onVisibilityAggregated(true); + } + + private ScrollCaptureTarget createTarget(ScrollCaptureCallback callback, Rect localVisibleRect, + Point positionInWindow, int scrollCaptureHint) { + View mockView = new View(getTargetContext()); + return createTargetWithView(mockView, callback, localVisibleRect, positionInWindow, + scrollCaptureHint); + } + + private ScrollCaptureTarget createTargetWithView(View view, ScrollCaptureCallback callback, + Rect localVisibleRect, Point positionInWindow, int scrollCaptureHint) { + setupTargetView(view, localVisibleRect, scrollCaptureHint); + return new ScrollCaptureTarget(view, localVisibleRect, positionInWindow, callback); + } + + + static class FakeView extends ViewGroup implements ViewParent { + FakeView(Context context, int l, int t, int r, int b, int id) { + super(context); + layout(l, t, r, b); + setId(id); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + } + } + + static class FakeScrollCaptureCallback implements ScrollCaptureCallback { + private final Executor mExecutor; + private Rect mScrollBounds; + private long mDelayMillis; + private int mOnScrollCaptureSearchCount; + FakeScrollCaptureCallback(Executor executor) { + mExecutor = executor; + } + public int getOnScrollCaptureSearchCount() { + return mOnScrollCaptureSearchCount; + } + + @Override + public void onScrollCaptureSearch(CancellationSignal signal, Consumer<Rect> onReady) { + mOnScrollCaptureSearchCount++; + run(() -> { + Rect b = getScrollBounds(); + onReady.accept(b); + }); + } + + @Override + public void onScrollCaptureStart(ScrollCaptureSession session, CancellationSignal signal, + Runnable onReady) { + run(onReady); + } + + @Override + public void onScrollCaptureImageRequest(ScrollCaptureSession session, + CancellationSignal signal, Rect captureArea, Consumer<Rect> onReady) { + run(() -> onReady.accept(captureArea)); + } + + @Override + public void onScrollCaptureEnd(Runnable onReady) { + run(onReady); + } + + public void setScrollBounds(@Nullable Rect scrollBounds) { + mScrollBounds = scrollBounds; + } + + public void setDelay(long delayMillis) { + mDelayMillis = delayMillis; + } + + protected Rect getScrollBounds() { + return mScrollBounds; + } + + protected void run(Runnable r) { + mExecutor.execute(() -> { + delay(); + r.run(); + }); + } + + protected void delay() { + if (mDelayMillis > 0) { + try { + Thread.sleep(mDelayMillis); + } catch (InterruptedException e) { + // Ignore + } + } + } + } + static class CallbackStub implements ScrollCaptureCallback { + @Override + public void onScrollCaptureSearch(@NonNull CancellationSignal signal, + @NonNull Consumer<Rect> onReady) { + } + + @Override + public void onScrollCaptureStart(@NonNull ScrollCaptureSession session, + @NonNull CancellationSignal signal, @NonNull Runnable onReady) { + } + + @Override + public void onScrollCaptureImageRequest(@NonNull ScrollCaptureSession session, + @NonNull CancellationSignal signal, @NonNull Rect captureArea, + Consumer<Rect> onReady) { + } + + @Override + public void onScrollCaptureEnd(@NonNull Runnable onReady) { + } + } +} diff --git a/core/tests/coretests/src/android/view/ScrollCaptureTargetResolverTest.java b/core/tests/coretests/src/android/view/ScrollCaptureTargetResolverTest.java deleted file mode 100644 index 8b21b8ecee89..000000000000 --- a/core/tests/coretests/src/android/view/ScrollCaptureTargetResolverTest.java +++ /dev/null @@ -1,498 +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 android.view; - -import static androidx.test.InstrumentationRegistry.getTargetContext; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; - -import android.annotation.Nullable; -import android.content.Context; -import android.graphics.Point; -import android.graphics.Rect; -import android.os.Handler; - -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.LinkedList; -import java.util.function.Consumer; - -/** - * Tests of {@link ScrollCaptureTargetResolver}. - */ -@RunWith(AndroidJUnit4.class) -public class ScrollCaptureTargetResolverTest { - - private static final long TEST_TIMEOUT_MS = 2000; - private static final long RESOLVER_TIMEOUT_MS = 1000; - - private Handler mHandler; - private TargetConsumer mTargetConsumer; - - @Before - public void setUp() { - mTargetConsumer = new TargetConsumer(); - mHandler = new Handler(getTargetContext().getMainLooper()); - } - - @Test(timeout = TEST_TIMEOUT_MS) - public void testEmptyQueue() throws InterruptedException { - ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(new LinkedList<>()); - resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer); - - // Test only - resolver.waitForResult(); - - ScrollCaptureTarget result = mTargetConsumer.getLastValue(); - assertNull("Expected null due to empty queue", result); - } - - @Test(timeout = TEST_TIMEOUT_MS) - public void testNoValidTargets() throws InterruptedException { - LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>(); - - // Supplies scrollBounds = null - FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(); - callback1.setScrollBounds(null); - ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50), - new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); - - // Supplies scrollBounds = empty rect - FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(); - callback2.setScrollBounds(new Rect()); - ScrollCaptureTarget target2 = createTarget(callback2, new Rect(20, 30, 40, 50), - new Point(0, 20), View.SCROLL_CAPTURE_HINT_INCLUDE); - - targetQueue.add(target1); - targetQueue.add(target2); - - ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue); - resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer); - - // Test only - resolver.waitForResult(); - - ScrollCaptureTarget result = mTargetConsumer.getLastValue(); - assertNull("Expected null due to no valid targets", result); - } - - @Test(timeout = TEST_TIMEOUT_MS) - public void testSingleTarget() throws InterruptedException { - FakeScrollCaptureCallback callback = new FakeScrollCaptureCallback(); - ScrollCaptureTarget target = createTarget(callback, - new Rect(20, 30, 40, 50), new Point(10, 10), - View.SCROLL_CAPTURE_HINT_AUTO); - callback.setScrollBounds(new Rect(2, 2, 18, 18)); - - LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>(); - targetQueue.add(target); - ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue); - resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer); - - // Test only - resolver.waitForResult(); - - ScrollCaptureTarget result = mTargetConsumer.getLastValue(); - assertSame("Excepted the same target as a result", target, result); - assertEquals("result has wrong scroll bounds", - new Rect(2, 2, 18, 18), result.getScrollBounds()); - } - - @Test(timeout = TEST_TIMEOUT_MS) - public void testSingleTarget_backgroundThread() throws InterruptedException { - BackgroundTestCallback callback1 = new BackgroundTestCallback(); - ScrollCaptureTarget target1 = createTarget(callback1, - new Rect(20, 30, 40, 50), new Point(10, 10), - View.SCROLL_CAPTURE_HINT_AUTO); - callback1.setDelay(100); - callback1.setScrollBounds(new Rect(2, 2, 18, 18)); - - LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>(); - targetQueue.add(target1); - - ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue); - resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer); - - // Test only - resolver.waitForResult(); - - ScrollCaptureTarget result = mTargetConsumer.getLastValue(); - assertSame("Excepted the single target1 as a result", target1, result); - assertEquals("Result has wrong scroll bounds", - new Rect(2, 2, 18, 18), result.getScrollBounds()); - } - - @Test(timeout = TEST_TIMEOUT_MS) - public void testPreferNonEmptyBounds() throws InterruptedException { - LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>(); - - FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(); - callback1.setScrollBounds(new Rect()); - ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50), - new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); - - FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(); - callback2.setScrollBounds(new Rect(0, 0, 20, 20)); - ScrollCaptureTarget target2 = createTarget(callback2, new Rect(20, 30, 40, 50), - new Point(0, 20), View.SCROLL_CAPTURE_HINT_INCLUDE); - - FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback(); - callback3.setScrollBounds(null); - ScrollCaptureTarget target3 = createTarget(callback3, new Rect(20, 30, 40, 50), - new Point(0, 40), View.SCROLL_CAPTURE_HINT_AUTO); - - targetQueue.add(target1); - targetQueue.add(target2); // scrollBounds not null or empty() - targetQueue.add(target3); - - ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue); - resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer); - resolver.waitForResult(); - - ScrollCaptureTarget result = mTargetConsumer.getLastValue(); - assertEquals("Expected " + target2 + " as a result", target2, result); - assertEquals("result has wrong scroll bounds", - new Rect(0, 0, 20, 20), result.getScrollBounds()); - } - - @Test(timeout = TEST_TIMEOUT_MS) - public void testPreferHintInclude() throws InterruptedException { - LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>(); - - FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(); - callback1.setScrollBounds(new Rect(0, 0, 20, 20)); - ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50), - new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); - - FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(); - callback2.setScrollBounds(new Rect(1, 1, 19, 19)); - ScrollCaptureTarget target2 = createTarget(callback2, new Rect(20, 30, 40, 50), - new Point(0, 20), View.SCROLL_CAPTURE_HINT_INCLUDE); - - FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback(); - callback3.setScrollBounds(new Rect(2, 2, 18, 18)); - ScrollCaptureTarget target3 = createTarget(callback3, new Rect(20, 30, 40, 50), - new Point(0, 40), View.SCROLL_CAPTURE_HINT_AUTO); - - targetQueue.add(target1); - targetQueue.add(target2); // * INCLUDE > AUTO - targetQueue.add(target3); - - ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue); - resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer); - - resolver.waitForResult(); - - ScrollCaptureTarget result = mTargetConsumer.getLastValue(); - assertEquals("input = " + targetQueue + " Expected " + target2 - + " as the result, due to hint=INCLUDE", target2, result); - assertEquals("result has wrong scroll bounds", - new Rect(1, 1, 19, 19), result.getScrollBounds()); - } - - @Test(timeout = TEST_TIMEOUT_MS) - public void testDescendantPreferred() throws InterruptedException { - LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>(); - - ViewGroup targetView1 = new FakeRootView(getTargetContext(), 0, 0, 60, 60); // 60x60 - ViewGroup targetView2 = new FakeRootView(getTargetContext(), 20, 30, 40, 50); // 20x20 - ViewGroup targetView3 = new FakeRootView(getTargetContext(), 5, 5, 15, 15); // 10x10 - - targetView1.addView(targetView2); - targetView2.addView(targetView3); - - // Create first target with an unrelated parent - FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(); - callback1.setScrollBounds(new Rect(0, 0, 60, 60)); - ScrollCaptureTarget target1 = createTargetWithView(targetView1, callback1, - new Rect(0, 0, 60, 60), - new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); - - // Create second target associated with a view within parent2 - FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(); - callback2.setScrollBounds(new Rect(0, 0, 20, 20)); - ScrollCaptureTarget target2 = createTargetWithView(targetView2, callback2, - new Rect(0, 0, 20, 20), - new Point(20, 30), View.SCROLL_CAPTURE_HINT_AUTO); - - // Create third target associated with a view within parent3 - FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback(); - callback3.setScrollBounds(new Rect(0, 0, 15, 15)); - ScrollCaptureTarget target3 = createTargetWithView(targetView3, callback3, - new Rect(0, 0, 15, 15), - new Point(25, 35), View.SCROLL_CAPTURE_HINT_AUTO); - - targetQueue.add(target1); // auto, 60x60 - targetQueue.add(target2); // auto, 20x20 - targetQueue.add(target3); // auto, 15x15 <- innermost scrollable - - ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue); - resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer); - - // Test only - resolver.waitForResult(); - - ScrollCaptureTarget result = mTargetConsumer.getLastValue(); - assertSame("Expected target3 as the result, due to relation", target3, result); - assertEquals("result has wrong scroll bounds", - new Rect(0, 0, 15, 15), result.getScrollBounds()); - } - - /** - * If a timeout expires, late results are ignored. - */ - @Test(timeout = TEST_TIMEOUT_MS) - public void testTimeout() throws InterruptedException { - LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>(); - - // callback 1, 10x10, hint=AUTO, responds immediately from bg thread - BackgroundTestCallback callback1 = new BackgroundTestCallback(); - callback1.setScrollBounds(new Rect(5, 5, 15, 15)); - ScrollCaptureTarget target1 = createTarget( - callback1, new Rect(20, 30, 40, 50), new Point(10, 10), - View.SCROLL_CAPTURE_HINT_AUTO); - targetQueue.add(target1); - - // callback 2, 20x20, hint=AUTO, responds after 5s from bg thread - BackgroundTestCallback callback2 = new BackgroundTestCallback(); - callback2.setScrollBounds(new Rect(0, 0, 20, 20)); - callback2.setDelay(5000); - ScrollCaptureTarget target2 = createTarget( - callback2, new Rect(20, 30, 40, 50), new Point(10, 10), - View.SCROLL_CAPTURE_HINT_AUTO); - targetQueue.add(target2); - - // callback 3, 20x20, hint=INCLUDE, responds after 10s from bg thread - BackgroundTestCallback callback3 = new BackgroundTestCallback(); - callback3.setScrollBounds(new Rect(0, 0, 20, 20)); - callback3.setDelay(10000); - ScrollCaptureTarget target3 = createTarget( - callback3, new Rect(20, 30, 40, 50), new Point(10, 10), - View.SCROLL_CAPTURE_HINT_INCLUDE); - targetQueue.add(target3); - - // callback 1 will be received - // callback 2 & 3 will be ignored due to timeout - - ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue); - resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer); - - resolver.waitForResult(); - - ScrollCaptureTarget result = mTargetConsumer.getLastValue(); - assertSame("Expected target1 as the result, due to timeouts of others", target1, result); - assertEquals("result has wrong scroll bounds", - new Rect(5, 5, 15, 15), result.getScrollBounds()); - assertEquals("callback1 should have been called", - 1, callback1.getOnScrollCaptureSearchCount()); - assertEquals("callback2 should have been called", - 1, callback2.getOnScrollCaptureSearchCount()); - assertEquals("callback3 should have been called", - 1, callback3.getOnScrollCaptureSearchCount()); - } - - @Test(timeout = TEST_TIMEOUT_MS) - public void testWithCallbackMultipleReplies() throws InterruptedException { - // Calls response methods 3 times each - RepeatingCaptureCallback callback1 = new RepeatingCaptureCallback(3); - callback1.setScrollBounds(new Rect(2, 2, 18, 18)); - ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50), - new Point(10, 10), View.SCROLL_CAPTURE_HINT_AUTO); - - FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(); - callback2.setScrollBounds(new Rect(0, 0, 20, 20)); - ScrollCaptureTarget target2 = createTarget(callback2, new Rect(20, 30, 40, 50), - new Point(10, 10), View.SCROLL_CAPTURE_HINT_AUTO); - - LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>(); - targetQueue.add(target1); - targetQueue.add(target2); - - ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue); - resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer); - - resolver.waitForResult(); - - ScrollCaptureTarget result = mTargetConsumer.getLastValue(); - assertSame("Expected target2 as the result, due to hint=INCLUDE", target2, result); - assertEquals("result has wrong scroll bounds", - new Rect(0, 0, 20, 20), result.getScrollBounds()); - assertEquals("callback1 should have been called once", - 1, callback1.getOnScrollCaptureSearchCount()); - assertEquals("callback2 should have been called once", - 1, callback2.getOnScrollCaptureSearchCount()); - } - - private static class TargetConsumer implements Consumer<ScrollCaptureTarget> { - volatile ScrollCaptureTarget mResult; - int mAcceptCount; - - ScrollCaptureTarget getLastValue() { - return mResult; - } - - int acceptCount() { - return mAcceptCount; - } - - @Override - public void accept(@Nullable ScrollCaptureTarget t) { - mAcceptCount++; - mResult = t; - } - } - - private void setupTargetView(View view, Rect localVisibleRect, int scrollCaptureHint) { - view.setScrollCaptureHint(scrollCaptureHint); - view.onVisibilityAggregated(true); - // Treat any offset as padding, outset localVisibleRect on all sides and use this as - // child bounds - Rect bounds = new Rect(localVisibleRect); - bounds.inset(-bounds.left, -bounds.top, bounds.left, bounds.top); - view.layout(bounds.left, bounds.top, bounds.right, bounds.bottom); - view.onVisibilityAggregated(true); - } - - private ScrollCaptureTarget createTarget(ScrollCaptureCallback callback, Rect localVisibleRect, - Point positionInWindow, int scrollCaptureHint) { - View mockView = new View(getTargetContext()); - return createTargetWithView(mockView, callback, localVisibleRect, positionInWindow, - scrollCaptureHint); - } - - private ScrollCaptureTarget createTargetWithView(View view, ScrollCaptureCallback callback, - Rect localVisibleRect, Point positionInWindow, int scrollCaptureHint) { - setupTargetView(view, localVisibleRect, scrollCaptureHint); - return new ScrollCaptureTarget(view, localVisibleRect, positionInWindow, callback); - } - - - static class FakeRootView extends ViewGroup implements ViewParent { - FakeRootView(Context context, int l, int t, int r, int b) { - super(context); - layout(l, t, r, b); - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - } - } - - static class FakeScrollCaptureCallback implements ScrollCaptureCallback { - private Rect mScrollBounds; - private long mDelayMillis; - private int mOnScrollCaptureSearchCount; - - public int getOnScrollCaptureSearchCount() { - return mOnScrollCaptureSearchCount; - } - - @Override - public void onScrollCaptureSearch(Consumer<Rect> onReady) { - mOnScrollCaptureSearchCount++; - run(() -> { - Rect b = getScrollBounds(); - onReady.accept(b); - }); - } - - @Override - public void onScrollCaptureStart(ScrollCaptureSession session, Runnable onReady) { - run(onReady); - } - - @Override - public void onScrollCaptureImageRequest(ScrollCaptureSession session, Rect captureArea) { - run(() -> session.notifyBufferSent(0, captureArea)); - } - - @Override - public void onScrollCaptureEnd(Runnable onReady) { - run(onReady); - } - - public void setScrollBounds(@Nullable Rect scrollBounds) { - mScrollBounds = scrollBounds; - } - - public void setDelay(long delayMillis) { - mDelayMillis = delayMillis; - } - - protected Rect getScrollBounds() { - return mScrollBounds; - } - - protected void run(Runnable r) { - delay(); - r.run(); - } - - protected void delay() { - if (mDelayMillis > 0) { - try { - Thread.sleep(mDelayMillis); - } catch (InterruptedException e) { - // Ignore - } - } - } - } - - static class RepeatingCaptureCallback extends FakeScrollCaptureCallback { - private int mRepeatCount; - - RepeatingCaptureCallback(int repeatCount) { - mRepeatCount = repeatCount; - } - - protected void run(Runnable r) { - delay(); - for (int i = 0; i < mRepeatCount; i++) { - r.run(); - } - } - } - - /** Response to async calls on an arbitrary background thread */ - static class BackgroundTestCallback extends FakeScrollCaptureCallback { - static int sCount = 0; - private void runOnBackgroundThread(Runnable r) { - final Runnable target = () -> { - delay(); - r.run(); - }; - Thread t = new Thread(target); - synchronized (BackgroundTestCallback.this) { - sCount++; - } - t.setName("Background-Thread-" + sCount); - t.start(); - } - - @Override - protected void run(Runnable r) { - runOnBackgroundThread(r); - } - } -} diff --git a/core/tests/coretests/src/android/view/SoundEffectConstantsTest.java b/core/tests/coretests/src/android/view/SoundEffectConstantsTest.java new file mode 100644 index 000000000000..45ffb1221515 --- /dev/null +++ b/core/tests/coretests/src/android/view/SoundEffectConstantsTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for {@link SoundEffectConstants} + * + * Build/Install/Run: + * atest FrameworksCoreTests:SoundEffectConstantsTest + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class SoundEffectConstantsTest { + + @Test + public void testIsNavigationRepeat() { + assertTrue(SoundEffectConstants.isNavigationRepeat( + SoundEffectConstants.NAVIGATION_REPEAT_RIGHT)); + assertTrue(SoundEffectConstants.isNavigationRepeat( + SoundEffectConstants.NAVIGATION_REPEAT_LEFT)); + assertTrue( + SoundEffectConstants.isNavigationRepeat(SoundEffectConstants.NAVIGATION_REPEAT_UP)); + assertTrue(SoundEffectConstants.isNavigationRepeat( + SoundEffectConstants.NAVIGATION_REPEAT_DOWN)); + assertFalse(SoundEffectConstants.isNavigationRepeat(SoundEffectConstants.NAVIGATION_RIGHT)); + assertFalse(SoundEffectConstants.isNavigationRepeat(SoundEffectConstants.NAVIGATION_LEFT)); + assertFalse(SoundEffectConstants.isNavigationRepeat(SoundEffectConstants.NAVIGATION_UP)); + assertFalse(SoundEffectConstants.isNavigationRepeat(SoundEffectConstants.NAVIGATION_DOWN)); + assertFalse(SoundEffectConstants.isNavigationRepeat(-1)); + } +} 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/android/view/TestScrollCaptureCallback.java b/core/tests/coretests/src/android/view/TestScrollCaptureCallback.java new file mode 100644 index 000000000000..1520c6e34a95 --- /dev/null +++ b/core/tests/coretests/src/android/view/TestScrollCaptureCallback.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import static org.junit.Assert.*; + +import android.graphics.Rect; +import android.os.CancellationSignal; + +import androidx.annotation.NonNull; + +import java.util.function.Consumer; + +class TestScrollCaptureCallback implements ScrollCaptureCallback { + private Consumer<Rect> mSearchConsumer; + private Runnable mStartOnReady; + private Consumer<Rect> mImageOnComplete; + private Runnable mOnEndReady; + private volatile int mModCount; + + @Override + public void onScrollCaptureSearch(@NonNull CancellationSignal signal, + @NonNull Consumer<Rect> onReady) { + mSearchConsumer = onReady; + mModCount++; + } + + @Override + public void onScrollCaptureStart(@NonNull ScrollCaptureSession session, + @NonNull CancellationSignal signal, @NonNull Runnable onReady) { + mStartOnReady = onReady; + mModCount++; + } + + @Override + public void onScrollCaptureImageRequest(@NonNull ScrollCaptureSession session, + @NonNull CancellationSignal signal, @NonNull Rect captureArea, + @NonNull Consumer<Rect> onComplete) { + mImageOnComplete = onComplete; + mModCount++; + } + + @Override + public void onScrollCaptureEnd(@NonNull Runnable onReady) { + mOnEndReady = onReady; + } + + void completeSearchRequest(Rect scrollBounds) { + assertNotNull("Did not receive search request", mSearchConsumer); + mSearchConsumer.accept(scrollBounds); + mModCount++; + } + + void verifyZeroInteractions() { + assertEquals("Expected zero interactions", 0, mModCount); + } + + void completeStartRequest() { + assertNotNull("Did not receive start request", mStartOnReady); + mStartOnReady.run(); + } + + void completeImageRequest(Rect captured) { + assertNotNull("Did not receive image request", mImageOnComplete); + mImageOnComplete.accept(captured); + } + + void completeEndRequest() { + assertNotNull("Did not receive end request", mOnEndReady); + mOnEndReady.run(); + } +} diff --git a/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java b/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java index 3af0533e763c..41cd4c562bd8 100644 --- a/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java +++ b/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java @@ -19,7 +19,9 @@ package android.view; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.testng.AssertJUnit.assertSame; @@ -27,19 +29,20 @@ import android.annotation.Nullable; import android.content.Context; import android.graphics.Point; import android.graphics.Rect; +import android.os.CancellationSignal; import android.platform.test.annotations.Presubmit; +import androidx.annotation.NonNull; import androidx.test.filters.FlakyTest; import androidx.test.filters.MediumTest; import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; -import java.util.LinkedList; -import java.util.Queue; +import java.util.concurrent.Executor; +import java.util.function.Consumer; /** * Exercises Scroll Capture search in {@link ViewGroup}. @@ -50,10 +53,7 @@ import java.util.Queue; @RunWith(MockitoJUnitRunner.class) public class ViewGroupScrollCaptureTest { - @Mock - ScrollCaptureCallback mMockCallback; - @Mock - ScrollCaptureCallback mMockCallback2; + private static final Executor DIRECT_EXECUTOR = Runnable::run; /** Make sure the hint flags are saved and loaded correctly. */ @Test @@ -103,25 +103,24 @@ public class ViewGroupScrollCaptureTest { public void testDispatchScrollCaptureSearch_noCallback_hintAuto() throws Exception { final Context context = getInstrumentation().getContext(); final MockViewGroup viewGroup = new MockViewGroup(context, 0, 0, 200, 200); + TestScrollCaptureCallback callback = new TestScrollCaptureCallback(); // When system internal scroll capture is requested, this callback is returned. - viewGroup.setScrollCaptureCallbackInternalForTest(mMockCallback); + viewGroup.setScrollCaptureCallbackInternalForTest(callback); Rect localVisibleRect = new Rect(0, 0, 200, 200); Point windowOffset = new Point(); - LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>(); + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR); // Dispatch - viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targetList); - - // Verify the system checked for fallback support - viewGroup.assertDispatchScrollCaptureCount(1); - viewGroup.assertLastDispatchScrollCaptureArgs(localVisibleRect, windowOffset); + viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results::addTarget); + callback.completeSearchRequest(new Rect(1, 2, 3, 4)); + assertTrue(results.isComplete()); // Verify the target is as expected. - assertEquals(1, targetList.size()); - ScrollCaptureTarget target = targetList.get(0); - assertSame("Target has the wrong callback", mMockCallback, target.getCallback()); + ScrollCaptureTarget target = results.getTopResult(); + assertNotNull("Target not found", target); + assertSame("Target has the wrong callback", callback, target.getCallback()); assertSame("Target has the wrong View", viewGroup, target.getContainingView()); assertEquals("Target hint is incorrect", View.SCROLL_CAPTURE_HINT_AUTO, target.getContainingView().getScrollCaptureHint()); @@ -139,18 +138,22 @@ public class ViewGroupScrollCaptureTest { final MockViewGroup viewGroup = new MockViewGroup(context, 0, 0, 200, 200, View.SCROLL_CAPTURE_HINT_EXCLUDE); + TestScrollCaptureCallback callback = new TestScrollCaptureCallback(); + // When system internal scroll capture is requested, this callback is returned. - viewGroup.setScrollCaptureCallbackInternalForTest(mMockCallback); + viewGroup.setScrollCaptureCallbackInternalForTest(callback); Rect localVisibleRect = new Rect(0, 0, 200, 200); Point windowOffset = new Point(); - LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>(); + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR); + assertTrue(results.isComplete()); // Dispatch - viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targetList); + viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results::addTarget); + callback.verifyZeroInteractions(); // Verify the results. - assertEquals("Target list size should be zero.", 0, targetList.size()); + assertTrue("Results should be empty.", results.isEmpty()); } /** @@ -164,27 +167,34 @@ public class ViewGroupScrollCaptureTest { final Context context = getInstrumentation().getContext(); MockViewGroup viewGroup = new MockViewGroup(context, 0, 0, 200, 200); + TestScrollCaptureCallback callback = new TestScrollCaptureCallback(); + TestScrollCaptureCallback callback2 = new TestScrollCaptureCallback(); + // With an already provided scroll capture callback - viewGroup.setScrollCaptureCallback(mMockCallback); + viewGroup.setScrollCaptureCallback(callback); // When system internal scroll capture is requested, this callback is returned. - viewGroup.setScrollCaptureCallbackInternalForTest(mMockCallback); + viewGroup.setScrollCaptureCallbackInternalForTest(callback2); Rect localVisibleRect = new Rect(0, 0, 200, 200); Point windowOffset = new Point(); - LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>(); + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR); // Dispatch to the ViewGroup - viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targetList); - - // Confirm that framework support was not requested, - // because this view already had a callback set. - viewGroup.assertCreateScrollCaptureCallbackInternalCount(0); + viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results::addTarget); + callback.completeSearchRequest(new Rect(1, 2, 3, 4)); // Verify the target is as expected. - assertEquals(1, targetList.size()); - ScrollCaptureTarget target = targetList.get(0); - assertSame("Target has the wrong callback", mMockCallback, target.getCallback()); + assertFalse(results.isEmpty()); + assertTrue(results.isComplete()); + + // internal framework callback was not requested + callback2.verifyZeroInteractions(); + + ScrollCaptureTarget target = results.getTopResult(); + + assertNotNull("Target not found", target); + assertSame("Target has the wrong callback", callback, target.getCallback()); assertSame("Target has the wrong View", viewGroup, target.getContainingView()); assertEquals("Target hint is incorrect", View.SCROLL_CAPTURE_HINT_AUTO, target.getContainingView().getScrollCaptureHint()); @@ -201,22 +211,22 @@ public class ViewGroupScrollCaptureTest { final Context context = getInstrumentation().getContext(); MockViewGroup viewGroup = new MockViewGroup(context, 0, 0, 200, 200, View.SCROLL_CAPTURE_HINT_EXCLUDE); + + TestScrollCaptureCallback callback = new TestScrollCaptureCallback(); + // With an already provided scroll capture callback - viewGroup.setScrollCaptureCallback(mMockCallback); + viewGroup.setScrollCaptureCallback(callback); Rect localVisibleRect = new Rect(0, 0, 200, 200); Point windowOffset = new Point(); - LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>(); + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR); // Dispatch to the ViewGroup itself - viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targetList); - - // Confirm that framework support was not requested, because this view is excluded. - // (And because this view has a callback set.) - viewGroup.assertCreateScrollCaptureCallbackInternalCount(0); + viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results::addTarget); + callback.verifyZeroInteractions(); // Has callback, but hint=excluded, so excluded. - assertTrue(targetList.isEmpty()); + assertNull(results.getTopResult()); } /** @@ -252,37 +262,43 @@ public class ViewGroupScrollCaptureTest { // | | | // +---------------+----------+ (200,200) - // View 1 is clipped and not visible. + // View 1 is fully clipped and not visible. final MockView view1 = new MockView(context, 0, 0, 200, 25); viewGroup.addView(view1); - // View 2 is partially visible. + // View 2 is partially visible. (75x75) final MockView view2 = new MockView(context, 0, 25, 150, 100); viewGroup.addView(view2); - // View 3 is partially visible. + TestScrollCaptureCallback callback1 = new TestScrollCaptureCallback(); + + // View 3 is partially visible (175x50) // Pretend View3 can scroll by having framework provide fallback support final MockView view3 = new MockView(context, 0, 100, 200, 200); // When system internal scroll capture is requested for this view, return this callback. - view3.setScrollCaptureCallbackInternalForTest(mMockCallback); + view3.setScrollCaptureCallbackInternalForTest(callback1); viewGroup.addView(view3); // View 4 is invisible and should be ignored. final MockView view4 = new MockView(context, 150, 25, 200, 100, View.INVISIBLE); viewGroup.addView(view4); - // View 4 is invisible and should be ignored. + TestScrollCaptureCallback callback2 = new TestScrollCaptureCallback(); + + // View 5 is partially visible and explicitly included via flag. (25x50) final MockView view5 = new MockView(context, 150, 100, 200, 200); - // When system internal scroll capture is requested for this view, return this callback. - view5.setScrollCaptureCallback(mMockCallback2); + view5.setScrollCaptureCallback(callback2); view5.setScrollCaptureHint(View.SCROLL_CAPTURE_HINT_INCLUDE); viewGroup.addView(view5); // Where targets are added - final LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>(); + final ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR); // Dispatch to the ViewGroup - viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targetList); + viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results::addTarget); + callback1.completeSearchRequest(new Rect(0, 0, 200, 100)); + callback2.completeSearchRequest(new Rect(0, 0, 50, 100)); + assertTrue(results.isComplete()); // View 1 is entirely clipped by the parent and not visible, dispatch // skips this view entirely. @@ -317,18 +333,14 @@ public class ViewGroupScrollCaptureTest { view5.assertCreateScrollCaptureCallbackInternalCount(0); // 2 views should have been returned, view3 & view5 - assertEquals(2, targetList.size()); - - ScrollCaptureTarget target = targetList.get(0); - assertSame("First target has the wrong View", view3, target.getContainingView()); - assertSame("First target has the wrong callback", mMockCallback, target.getCallback()); - assertEquals("First target hint is incorrect", View.SCROLL_CAPTURE_HINT_AUTO, - target.getContainingView().getScrollCaptureHint()); - - target = targetList.get(1); - assertSame("Second target has the wrong View", view5, target.getContainingView()); - assertSame("Second target has the wrong callback", mMockCallback2, target.getCallback()); - assertEquals("Second target hint is incorrect", View.SCROLL_CAPTURE_HINT_INCLUDE, + assertFalse(results.isEmpty()); + assertTrue(results.isComplete()); + + ScrollCaptureTarget target = results.getTopResult(); + assertNotNull("Target not found", target); + assertSame("Result is the wrong View", view5, target.getContainingView()); + assertSame("Result is the wrong callback", callback2, target.getCallback()); + assertEquals("First target hint is incorrect", View.SCROLL_CAPTURE_HINT_INCLUDE, target.getContainingView().getScrollCaptureHint()); } @@ -371,7 +383,7 @@ public class ViewGroupScrollCaptureTest { } void assertCreateScrollCaptureCallbackInternalCount(int count) { - assertEquals("Unexpected number of calls to createScrollCaptureCallackInternal", + assertEquals("Unexpected number of calls to createScrollCaptureCallbackInternal", count, mCreateScrollCaptureCallbackInternalCount); } @@ -385,11 +397,11 @@ public class ViewGroupScrollCaptureTest { @Override public void dispatchScrollCaptureSearch(Rect localVisibleRect, Point windowOffset, - Queue<ScrollCaptureTarget> targets) { + Consumer<ScrollCaptureTarget> results) { mDispatchScrollCaptureSearchNumCalls++; mDispatchScrollCaptureSearchLastLocalVisibleRect = new Rect(localVisibleRect); mDispatchScrollCaptureSearchLastWindowOffset = new Point(windowOffset); - super.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targets); + super.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results); } @Override @@ -401,13 +413,31 @@ public class ViewGroupScrollCaptureTest { } } + static class CallbackStub implements ScrollCaptureCallback { + + @Override + public void onScrollCaptureSearch(@NonNull CancellationSignal signal, + @NonNull Consumer<Rect> onReady) { + } + + @Override + public void onScrollCaptureStart(@NonNull ScrollCaptureSession session, + @NonNull CancellationSignal signal, @NonNull Runnable onReady) { + } + + @Override + public void onScrollCaptureImageRequest(@NonNull ScrollCaptureSession session, + @NonNull CancellationSignal signal, @NonNull Rect captureArea, + Consumer<Rect> onComplete) { + } + + @Override + public void onScrollCaptureEnd(@NonNull Runnable onReady) { + } + }; + public static final class MockViewGroup extends ViewGroup { private ScrollCaptureCallback mInternalCallback; - private int mDispatchScrollCaptureSearchNumCalls; - private Rect mDispatchScrollCaptureSearchLastLocalVisibleRect; - private Point mDispatchScrollCaptureSearchLastWindowOffset; - private int mCreateScrollCaptureCallbackInternalCount; - MockViewGroup(Context context) { this(context, /* left */ 0, /* top */0, /* right */ 0, /* bottom */0); @@ -428,16 +458,10 @@ public class ViewGroupScrollCaptureTest { mInternalCallback = internal; } - void assertDispatchScrollCaptureSearchCount(int count) { - assertEquals("Unexpected number of calls to dispatchScrollCaptureSearch", - count, mDispatchScrollCaptureSearchNumCalls); - } - @Override @Nullable public ScrollCaptureCallback createScrollCaptureCallbackInternal(Rect localVisibleRect, Point offsetInWindow) { - mCreateScrollCaptureCallbackInternalCount++; return mInternalCallback; } @@ -445,36 +469,5 @@ public class ViewGroupScrollCaptureTest { protected void onLayout(boolean changed, int l, int t, int r, int b) { // We don't layout this view. } - - void assertDispatchScrollCaptureCount(int count) { - assertEquals(count, mDispatchScrollCaptureSearchNumCalls); - } - - void assertLastDispatchScrollCaptureArgs(Rect localVisibleRect, Point windowOffset) { - assertEquals("arg localVisibleRect to dispatchScrollCaptureCallback was incorrect.", - localVisibleRect, mDispatchScrollCaptureSearchLastLocalVisibleRect); - assertEquals("arg windowOffset to dispatchScrollCaptureCallback was incorrect.", - windowOffset, mDispatchScrollCaptureSearchLastWindowOffset); - } - void assertCreateScrollCaptureCallbackInternalCount(int count) { - assertEquals("Unexpected number of calls to createScrollCaptureCallackInternal", - count, mCreateScrollCaptureCallbackInternalCount); - } - - void reset() { - mDispatchScrollCaptureSearchNumCalls = 0; - mDispatchScrollCaptureSearchLastWindowOffset = null; - mDispatchScrollCaptureSearchLastLocalVisibleRect = null; - mCreateScrollCaptureCallbackInternalCount = 0; - } - - @Override - public void dispatchScrollCaptureSearch(Rect localVisibleRect, Point windowOffset, - Queue<ScrollCaptureTarget> targets) { - mDispatchScrollCaptureSearchNumCalls++; - mDispatchScrollCaptureSearchLastLocalVisibleRect = new Rect(localVisibleRect); - mDispatchScrollCaptureSearchLastWindowOffset = new Point(windowOffset); - super.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targets); - } } } diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index c67174f0ae1e..7746bc2e273a 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -207,7 +207,7 @@ public class ViewRootImplTest { final CountDownLatch latch = new CountDownLatch(1); mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureCallbacks.Default() { @Override - public void onUnavailable() { + public void onScrollCaptureResponse(ScrollCaptureResponse response) { latch.countDown(); } }); @@ -220,6 +220,37 @@ public class ViewRootImplTest { } /** + * Ensure scroll capture request handles a ViewRootImpl with no view tree. + */ + @Test + public void requestScrollCapture_timeout() { + final View view = new View(mContext); + view.setScrollCaptureCallback(new TestScrollCaptureCallback()); // Does nothing + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + WindowManager.LayoutParams wmlp = + new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); + // Set a fake token to bypass 'is your activity running' check + wmlp.token = new Binder(); + view.setLayoutParams(wmlp); + mViewRootImpl.setView(view, wmlp, null); + }); + + final CountDownLatch latch = new CountDownLatch(1); + mViewRootImpl.setScrollCaptureRequestTimeout(100); + mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureCallbacks.Default() { + @Override + public void onScrollCaptureResponse(ScrollCaptureResponse response) { + latch.countDown(); + } + }); + try { + if (!latch.await(2500, TimeUnit.MILLISECONDS)) { + fail("requestScrollCapture timeout did not occur"); + } + } catch (InterruptedException e) { /* ignore */ } + } + + /** * When window doesn't have focus, keys should be dropped. */ @Test diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java index b819d9edb2a8..7b7cf65f89ed 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java @@ -40,6 +40,7 @@ import org.junit.runners.Suite; BatteryStatsTimeBaseTest.class, BatteryStatsTimerTest.class, BatteryStatsUidTest.class, + BatteryUsageStatsProviderTest.class, BatteryUsageStatsTest.class, BatteryStatsUserLifecycleTests.class, BluetoothPowerCalculatorTest.class, @@ -71,6 +72,7 @@ import org.junit.runners.Suite; UserPowerCalculatorTest.class, VideoPowerCalculatorTest.class, WakelockPowerCalculatorTest.class, + WifiPowerCalculatorTest.class, com.android.internal.power.MeasuredEnergyStatsTest.class }) diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java new file mode 100644 index 000000000000..c4b7796b49cf --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java @@ -0,0 +1,76 @@ +/* + * 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.os; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.ActivityManager; +import android.content.Context; +import android.os.BatteryUsageStats; +import android.os.BatteryUsageStatsQuery; +import android.os.Process; +import android.os.UidBatteryConsumer; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.List; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class BatteryUsageStatsProviderTest { + private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; + private static final long MINUTE_IN_MS = 60 * 1000; + + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(); + + @Test + public void test_getBatteryUsageStats() { + BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + + batteryStats.noteActivityResumedLocked(APP_UID, + 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS); + batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_TOP, + 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS); + batteryStats.noteActivityPausedLocked(APP_UID, + 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS); + batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_SERVICE, + 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS); + batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_CACHED_EMPTY, + 40 * MINUTE_IN_MS, 40 * MINUTE_IN_MS); + + Context context = InstrumentationRegistry.getContext(); + BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, batteryStats); + + final BatteryUsageStats batteryUsageStats = + provider.getBatteryUsageStats(BatteryUsageStatsQuery.DEFAULT); + + final List<UidBatteryConsumer> uidBatteryConsumers = + batteryUsageStats.getUidBatteryConsumers(); + final UidBatteryConsumer uidBatteryConsumer = uidBatteryConsumers.get(0); + assertThat(uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND)) + .isEqualTo(20 * MINUTE_IN_MS); + assertThat(uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND)) + .isEqualTo(10 * MINUTE_IN_MS); + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java index 355ac6dbcc00..9ef628848beb 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java @@ -66,33 +66,36 @@ public class BatteryUsageStatsTest { final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks); final BatteryStatsImpl.Uid batteryStatsUid = batteryStats.getUidStatsLocked(2000); - final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(1, 1); - builder.setDischargePercentage(20); - builder.setDischargedPowerRange(1000, 2000); - - final UidBatteryConsumer.Builder uidBatteryConsumerBuilder = - builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid); - uidBatteryConsumerBuilder.setPackageWithHighestDrain("foo"); - uidBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, 300); - uidBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 400); - uidBatteryConsumerBuilder.setConsumedPowerForCustomComponent( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 500); - uidBatteryConsumerBuilder.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, 600); - uidBatteryConsumerBuilder.setUsageDurationMillis( - BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND, 700); - uidBatteryConsumerBuilder.setUsageDurationForCustomComponentMillis( - BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 800); - - final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder = - builder.getOrCreateSystemBatteryConsumerBuilder( - SystemBatteryConsumer.DRAIN_TYPE_CAMERA); - systemBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 10100); - systemBatteryConsumerBuilder.setConsumedPowerForCustomComponent( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200); - systemBatteryConsumerBuilder.setUsageDurationMillis( - BatteryConsumer.TIME_COMPONENT_CPU, 10300); - systemBatteryConsumerBuilder.setUsageDurationForCustomComponentMillis( - BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 10400); + final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(1, 1) + .setDischargePercentage(20) + .setDischargedPowerRange(1000, 2000); + + builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid) + .setPackageWithHighestDrain("foo") + .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, 1000) + .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, 2000) + .setConsumedPower( + BatteryConsumer.POWER_COMPONENT_USAGE, 300) + .setConsumedPower( + BatteryConsumer.POWER_COMPONENT_CPU, 400) + .setConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 500) + .setUsageDurationMillis( + BatteryConsumer.TIME_COMPONENT_CPU, 600) + .setUsageDurationMillis( + BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND, 700) + .setUsageDurationForCustomComponentMillis( + BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 800); + + builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_CAMERA) + .setConsumedPower( + BatteryConsumer.POWER_COMPONENT_CPU, 10100) + .setConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200) + .setUsageDurationMillis( + BatteryConsumer.TIME_COMPONENT_CPU, 10300) + .setUsageDurationForCustomComponentMillis( + BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 10400); return builder.build(); } @@ -108,6 +111,10 @@ public class BatteryUsageStatsTest { for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) { if (uidBatteryConsumer.getUid() == 2000) { assertThat(uidBatteryConsumer.getPackageWithHighestDrain()).isEqualTo("foo"); + assertThat(uidBatteryConsumer.getTimeInStateMs( + UidBatteryConsumer.STATE_FOREGROUND)).isEqualTo(1000); + assertThat(uidBatteryConsumer.getTimeInStateMs( + UidBatteryConsumer.STATE_BACKGROUND)).isEqualTo(2000); assertThat(uidBatteryConsumer.getConsumedPower( BatteryConsumer.POWER_COMPONENT_USAGE)).isEqualTo(300); assertThat(uidBatteryConsumer.getConsumedPower( diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java new file mode 100644 index 000000000000..e1005457c289 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java @@ -0,0 +1,128 @@ +/* + * 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.os; + + +import static com.google.common.truth.Truth.assertThat; + +import android.net.NetworkCapabilities; +import android.net.NetworkStats; +import android.os.BatteryConsumer; +import android.os.Process; +import android.os.SystemBatteryConsumer; +import android.os.UidBatteryConsumer; +import android.os.WorkSource; +import android.os.connectivity.WifiActivityEnergyInfo; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class WifiPowerCalculatorTest { + private static final double PRECISION = 0.00001; + + private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; + + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE, 360.0) + .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX, 480.0) + .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX, 720.0) + .setAveragePower(PowerProfile.POWER_WIFI_ON, 360.0) + .setAveragePower(PowerProfile.POWER_WIFI_SCAN, 480.0) + .setAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, 720.0) + .setAveragePower(PowerProfile.POWER_WIFI_ACTIVE, 1080.0); + + @Test + public void testPowerControllerBasedModel() { + BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + + batteryStats.noteNetworkInterfaceForTransports("wifi", + new int[]{NetworkCapabilities.TRANSPORT_WIFI}); + + NetworkStats networkStats = new NetworkStats(10000, 1) + .insertEntry("wifi", APP_UID, 0, 0, 1000, 100, 2000, 20, 100) + .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111); + mStatsRule.setNetworkStats(networkStats); + + WifiActivityEnergyInfo energyInfo = new WifiActivityEnergyInfo(10000, + WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 1000, 2000, 3000, 4000); + + batteryStats.updateWifiState(energyInfo, 1000, 1000); + + WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile()); + mStatsRule.apply(calculator); + + UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); + assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + .isEqualTo(1423); + assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) + .isWithin(PRECISION).of(0.2214666); + + SystemBatteryConsumer systemConsumer = + mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI); + assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + .isEqualTo(5577); + assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) + .isWithin(PRECISION).of(0.645200); + } + + @Test + public void testTimerBasedModel() { + BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + + batteryStats.noteNetworkInterfaceForTransports("wifi", + new int[]{NetworkCapabilities.TRANSPORT_WIFI}); + + NetworkStats networkStats = new NetworkStats(10000, 1) + .insertEntry("wifi", APP_UID, 0, 0, 1000, 100, 2000, 20, 100) + .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111); + mStatsRule.setNetworkStats(networkStats); + + batteryStats.noteWifiScanStartedLocked(APP_UID, 1000, 1000); + batteryStats.noteWifiScanStoppedLocked(APP_UID, 2000, 2000); + batteryStats.noteWifiRunningLocked(new WorkSource(APP_UID), 3000, 3000); + batteryStats.noteWifiStoppedLocked(new WorkSource(APP_UID), 4000, 4000); + batteryStats.noteWifiRunningLocked(new WorkSource(Process.WIFI_UID), 1111, 2222); + batteryStats.noteWifiStoppedLocked(new WorkSource(Process.WIFI_UID), 3333, 4444); + + // Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively + // on the packet counts. + batteryStats.updateWifiState(/* energyInfo */ null, 1000, 1000); + + WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile()); + mStatsRule.apply(calculator); + + UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); + assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + .isEqualTo(1000); + assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) + .isWithin(PRECISION).of(0.8231573); + + SystemBatteryConsumer systemConsumer = + mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI); + assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + .isEqualTo(2222); + assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) + .isWithin(PRECISION).of(0.8759216); + } +} diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java index 5fd5a7838c3a..d217bce24b9c 100644 --- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java @@ -81,11 +81,11 @@ public class MeasuredEnergyStatsTest { final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); - stats.updateCustomBucket(0, 50, true); - stats.updateCustomBucket(1, 60, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40); + stats.updateCustomBucket(0, 50); + stats.updateCustomBucket(1, 60); final MeasuredEnergyStats newStats = MeasuredEnergyStats.createFromTemplate(stats); @@ -114,11 +114,11 @@ public class MeasuredEnergyStatsTest { final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); - stats.updateCustomBucket(0, 50, true); - stats.updateCustomBucket(1, 60, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40); + stats.updateCustomBucket(0, 50); + stats.updateCustomBucket(1, 60); final Parcel parcel = Parcel.obtain(); stats.writeToParcel(parcel); @@ -149,11 +149,11 @@ public class MeasuredEnergyStatsTest { final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); - stats.updateCustomBucket(0, 50, true); - stats.updateCustomBucket(1, 60, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40); + stats.updateCustomBucket(0, 50); + stats.updateCustomBucket(1, 60); final Parcel parcel = Parcel.obtain(); MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false); @@ -185,17 +185,17 @@ public class MeasuredEnergyStatsTest { final MeasuredEnergyStats template = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); - template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); - template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); - template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); - template.updateCustomBucket(0, 50, true); + template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10); + template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5); + template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40); + template.updateCustomBucket(0, 50); final MeasuredEnergyStats stats = MeasuredEnergyStats.createFromTemplate(template); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 7, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 63, true); - stats.updateCustomBucket(0, 315, true); - stats.updateCustomBucket(1, 316, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 7); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 63); + stats.updateCustomBucket(0, 315); + stats.updateCustomBucket(1, 316); final Parcel parcel = Parcel.obtain(); MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false); @@ -243,8 +243,8 @@ public class MeasuredEnergyStatsTest { final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); // Accumulate energy in one bucket and one custom bucket, the rest should be zero - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200, true); - stats.updateCustomBucket(1, 60, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200); + stats.updateCustomBucket(1, 60); // Let's try parcelling with including zeros final Parcel includeZerosParcel = Parcel.obtain(); @@ -305,11 +305,11 @@ public class MeasuredEnergyStatsTest { final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); - stats.updateCustomBucket(0, 50, true); - stats.updateCustomBucket(1, 60, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40); + stats.updateCustomBucket(0, 50); + stats.updateCustomBucket(1, 60); final Parcel parcel = Parcel.obtain(); MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false); @@ -331,14 +331,14 @@ public class MeasuredEnergyStatsTest { final MeasuredEnergyStats template = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); - template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); - template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); - template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); - template.updateCustomBucket(0, 50, true); + template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10); + template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5); + template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40); + template.updateCustomBucket(0, 50); final MeasuredEnergyStats stats = MeasuredEnergyStats.createFromTemplate(template); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 0L, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 7L, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 0L); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 7L); final Parcel parcel = Parcel.obtain(); MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false); @@ -369,14 +369,14 @@ public class MeasuredEnergyStatsTest { final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_DOZE, 30, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_DOZE, 30); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5); - stats.updateCustomBucket(0, 50, true); - stats.updateCustomBucket(1, 60, true); - stats.updateCustomBucket(0, 3, true); + stats.updateCustomBucket(0, 50); + stats.updateCustomBucket(1, 60); + stats.updateCustomBucket(0, 3); assertEquals(15, stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_ON)); assertEquals(ENERGY_DATA_UNAVAILABLE, @@ -409,10 +409,10 @@ public class MeasuredEnergyStatsTest { final MeasuredEnergyStats stats = new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], 3); - stats.updateCustomBucket(0, 50, true); - stats.updateCustomBucket(1, 60, true); - stats.updateCustomBucket(2, 13, true); - stats.updateCustomBucket(1, 70, true); + stats.updateCustomBucket(0, 50); + stats.updateCustomBucket(1, 60); + stats.updateCustomBucket(2, 13); + stats.updateCustomBucket(1, 70); final long[] output = stats.getAccumulatedCustomBucketEnergies(); assertEquals(3, output.length); @@ -449,11 +449,11 @@ public class MeasuredEnergyStatsTest { final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); - stats.updateCustomBucket(0, 50, true); - stats.updateCustomBucket(1, 60, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40); + stats.updateCustomBucket(0, 50); + stats.updateCustomBucket(1, 60); MeasuredEnergyStats.resetIfNotNull(stats); // All energy should be reset to 0 @@ -471,10 +471,10 @@ public class MeasuredEnergyStatsTest { } // Values should increase as usual. - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 70, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 70); assertEquals(70L, stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_ON)); - stats.updateCustomBucket(1, 12, true); + stats.updateCustomBucket(1, 12); assertEquals(12L, stats.getAccumulatedCustomBucketEnergy(1)); } diff --git a/core/tests/coretests/src/com/android/internal/view/OWNERS b/core/tests/coretests/src/com/android/internal/view/OWNERS new file mode 100644 index 000000000000..1dad10de5ac7 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/view/OWNERS @@ -0,0 +1,3 @@ +# Scroll Capture +per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS +per-file *CaptureHelper*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 8fd5d804adc0..3900d7e674ca 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -406,8 +406,6 @@ applications that come with the platform <permission name="android.permission.SET_WALLPAPER" /> <permission name="android.permission.SET_WALLPAPER_COMPONENT" /> <permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" /> - <!-- Permissions required for Incremental CTS tests --> - <permission name="com.android.permission.USE_INSTALLER_V2"/> <permission name="android.permission.LOADER_USAGE_STATS"/> <!-- Permission required to test system only camera devices. --> <permission name="android.permission.SYSTEM_CAMERA" /> 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/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 32c777cf498c..c80788269c24 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -172,9 +172,9 @@ public class Typeface { * @hide */ @UnsupportedAppUsage - public long native_instance; + public final long native_instance; - private Runnable mCleaner; + private final Runnable mCleaner; /** @hide */ @IntDef(value = {NORMAL, BOLD, ITALIC, BOLD_ITALIC}) @@ -189,9 +189,9 @@ public class Typeface { /** @hide */ public static final int STYLE_MASK = 0x03; @UnsupportedAppUsage - private @Style int mStyle = 0; + private @Style final int mStyle; - private @IntRange(from = 0, to = FontStyle.FONT_WEIGHT_MAX) int mWeight = 0; + private @IntRange(from = 0, to = FontStyle.FONT_WEIGHT_MAX) final int mWeight; // Value for weight and italic. Indicates the value is resolved by font metadata. // Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp @@ -207,6 +207,7 @@ public class Typeface { private static final int STYLE_NORMAL = 0; private static final int STYLE_ITALIC = 1; + @GuardedBy("this") private int[] mSupportedAxes; private static final int[] EMPTY_AXES = {}; diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl index d00f5f669594..684eebe6ffde 100644 --- a/keystore/java/android/security/IKeyChainService.aidl +++ b/keystore/java/android/security/IKeyChainService.aidl @@ -63,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 f0bcfe52686d..65a81cd57f41 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -423,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. */ @@ -591,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. * 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/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/BubbleFlyoutView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java index 19c3cf9c462a..7d5c9f07f86c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java @@ -227,24 +227,29 @@ public class BubbleFlyoutView extends FrameLayout { /* * Fade animation for consecutive flyouts. */ - void animateUpdate(Bubble.FlyoutMessage flyoutMessage, float parentWidth, float stackY) { + void animateUpdate(Bubble.FlyoutMessage flyoutMessage, float parentWidth, PointF stackPos) { final Runnable afterFadeOut = () -> { updateFlyoutMessage(flyoutMessage, parentWidth); // Wait for TextViews to layout with updated height. post(() -> { - mFlyoutY = stackY + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f; - fade(true /* in */, () -> {} /* after */); + fade(true /* in */, stackPos, () -> {} /* after */); } /* after */ ); }; - fade(false /* in */, afterFadeOut); + fade(false /* in */, stackPos, afterFadeOut); } /* * Fade-out above or fade-in from below. */ - private void fade(boolean in, Runnable afterFade) { + private void fade(boolean in, PointF stackPos, Runnable afterFade) { + mFlyoutY = stackPos.y + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f; + setAlpha(in ? 0f : 1f); setTranslationY(in ? mFlyoutY + FLYOUT_FADE_Y : mFlyoutY); + mRestingTranslationX = mArrowPointingLeft + ? stackPos.x + mBubbleSize + mFlyoutSpaceFromBubble + : stackPos.x - getWidth() - mFlyoutSpaceFromBubble; + setTranslationX(mRestingTranslationX); animate() .alpha(in ? 1f : 0f) .setDuration(in ? FLYOUT_FADE_IN_DURATION : FLYOUT_FADE_OUT_DURATION) 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 a8ab4064455c..e99669f5b5e0 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 @@ -2431,7 +2431,7 @@ public class BubbleStackView extends FrameLayout if (mFlyout.getVisibility() == View.VISIBLE) { mFlyout.animateUpdate(bubble.getFlyoutMessage(), getWidth(), - mStackAnimationController.getStackPosition().y); + mStackAnimationController.getStackPosition()); } else { mFlyout.setVisibility(INVISIBLE); mFlyout.setupFlyoutStartingAsDot(bubble.getFlyoutMessage(), 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..f118b1e0b7a3 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; @@ -49,6 +50,7 @@ import com.android.internal.R; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Objects; /** * Contains information about the layout-properties of a display. This refers to internal layout @@ -81,6 +83,31 @@ public class DisplayLayout { private boolean mHasStatusBar = false; private int mNavBarFrameHeight = 0; + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof DisplayLayout)) return false; + final DisplayLayout other = (DisplayLayout) o; + return mUiMode == other.mUiMode + && mWidth == other.mWidth + && mHeight == other.mHeight + && Objects.equals(mCutout, other.mCutout) + && mRotation == other.mRotation + && mDensityDpi == other.mDensityDpi + && Objects.equals(mNonDecorInsets, other.mNonDecorInsets) + && Objects.equals(mStableInsets, other.mStableInsets) + && mHasNavigationBar == other.mHasNavigationBar + && mHasStatusBar == other.mHasStatusBar + && mNavBarFrameHeight == other.mNavBarFrameHeight; + } + + @Override + public int hashCode() { + return Objects.hash(mUiMode, mWidth, mHeight, mCutout, mRotation, mDensityDpi, + mNonDecorInsets, mStableInsets, mHasNavigationBar, mHasStatusBar, + mNavBarFrameHeight); + } + /** * Create empty layout. */ @@ -241,38 +268,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 +354,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/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java index fb70cbe502b0..4bb8e9b6581f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java @@ -39,12 +39,12 @@ import android.view.IWindowSession; import android.view.IWindowSessionCallback; import android.view.InsetsSourceControl; import android.view.InsetsState; +import android.view.ScrollCaptureResponse; import android.view.SurfaceControl; -import android.view.SurfaceSession; import android.view.SurfaceControlViewHost; +import android.view.SurfaceSession; import android.view.View; import android.view.ViewGroup; -import android.view.ViewRootImpl; import android.view.WindowManager; import android.view.WindowlessWindowManager; import android.window.ClientWindowFrames; @@ -371,7 +371,11 @@ public class SystemWindows { @Override public void requestScrollCapture(IScrollCaptureCallbacks callbacks) { try { - callbacks.onUnavailable(); + callbacks.onScrollCaptureResponse( + new ScrollCaptureResponse.Builder() + .setDescription("Not Implemented") + .build()); + } catch (RemoteException ex) { // ignore } 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..a52db24aa184 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; @@ -90,15 +90,15 @@ public class PipAnimationController { private final PipSurfaceTransactionHelper mSurfaceTransactionHelper; - private PipTransitionAnimator mCurrentAnimator; - - private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal = + private final ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal = ThreadLocal.withInitial(() -> { AnimationHandler handler = new AnimationHandler(); handler.setProvider(new SfVsyncFrameCallbackProvider()); return handler; }); + private PipTransitionAnimator mCurrentAnimator; + public PipAnimationController(PipSurfaceTransactionHelper helper) { mSurfaceTransactionHelper = helper; } @@ -268,6 +268,7 @@ public class PipAnimationController { if (mPipAnimationCallback != null) { mPipAnimationCallback.onPipAnimationEnd(mTaskInfo, tx, this); } + mTransitionDirection = TRANSITION_DIRECTION_NONE; } @Override @@ -275,6 +276,7 @@ public class PipAnimationController { if (mPipAnimationCallback != null) { mPipAnimationCallback.onPipAnimationCancel(mTaskInfo, this); } + mTransitionDirection = TRANSITION_DIRECTION_NONE; } @Override public void onAnimationRepeat(Animator animation) {} @@ -448,7 +450,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/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 4a2a032d8d1c..9a584c67f97c 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 @@ -65,6 +65,7 @@ import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.PipUtils; import java.io.PrintWriter; +import java.util.Objects; import java.util.function.Consumer; /** @@ -93,17 +94,20 @@ 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. */ private final DisplayChangeController.OnDisplayChangingListener mRotationController = ( int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) -> { - if (!mPipTaskOrganizer.isInPip() || mPipTaskOrganizer.isDeferringEnterPipAnimation()) { - // Skip if we aren't in PIP or haven't actually entered PIP yet. We still need to update - // the display layout in the bounds handler in this case. + if (!mPipTaskOrganizer.isInPip() + || mPipBoundsState.getDisplayLayout().rotation() == toRotation + || mPipTaskOrganizer.isDeferringEnterPipAnimation()) { + // Skip if the same rotation has been set or we aren't in PIP or haven't actually + // entered PIP yet. We still need to update the display layout in the bounds handler + // in this case. onDisplayRotationChangedNotInPip(mContext, toRotation); // do not forget to update the movement bounds as well. updateMovementBounds(mPipBoundsState.getNormalBounds(), true /* fromRotation */, @@ -178,8 +182,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 +314,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); } @@ -378,6 +382,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb } private void onDisplayChanged(DisplayLayout layout, boolean saveRestoreSnapFraction) { + if (Objects.equals(layout, mPipBoundsState.getDisplayLayout())) { + return; + } Runnable updateDisplayLayout = () -> { mPipBoundsState.setDisplayLayout(layout); updateMovementBounds(null /* toBounds */, @@ -476,8 +483,11 @@ public class PipController implements PipTransitionController.PipTransitionCallb int launcherRotation, int shelfHeight) { setShelfHeightLocked(shelfHeight > 0 /* visible */, shelfHeight); onDisplayRotationChangedNotInPip(mContext, launcherRotation); - return mPipTaskOrganizer.startSwipePipToHome(componentName, activityInfo, + final Rect entryBounds = mPipTaskOrganizer.startSwipePipToHome(componentName, activityInfo, pictureInPictureParams); + // sync mPipBoundsState with the newly calculated bounds. + mPipBoundsState.setNormalBounds(entryBounds); + return entryBounds; } private void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) { 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/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 10aea519f18b..35bab7aaf22c 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,8 +18,6 @@ package com.android.wm.shell.flicker import android.graphics.Region import android.view.Surface -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 @@ -32,15 +30,15 @@ fun FlickerTestParameter.appPairsDividerIsVisible() { fun FlickerTestParameter.appPairsDividerIsInvisible() { assertLayersEnd { - this.notExists(APP_PAIR_SPLIT_DIVIDER) + this.notContains(APP_PAIR_SPLIT_DIVIDER) } } fun FlickerTestParameter.appPairsDividerBecomesVisible() { assertLayers { - this.hidesLayer(DOCKED_STACK_DIVIDER) + this.isInvisible(DOCKED_STACK_DIVIDER) .then() - .showsLayer(DOCKED_STACK_DIVIDER) + .isVisible(DOCKED_STACK_DIVIDER) } } @@ -52,30 +50,30 @@ fun FlickerTestParameter.dockedStackDividerIsVisible() { fun FlickerTestParameter.dockedStackDividerBecomesVisible() { assertLayers { - this.hidesLayer(DOCKED_STACK_DIVIDER) + this.isInvisible(DOCKED_STACK_DIVIDER) .then() - .showsLayer(DOCKED_STACK_DIVIDER) + .isVisible(DOCKED_STACK_DIVIDER) } } fun FlickerTestParameter.dockedStackDividerBecomesInvisible() { assertLayers { - this.showsLayer(DOCKED_STACK_DIVIDER) + this.isVisible(DOCKED_STACK_DIVIDER) .then() - .hidesLayer(DOCKED_STACK_DIVIDER) + .isInvisible(DOCKED_STACK_DIVIDER) } } fun FlickerTestParameter.dockedStackDividerIsInvisible() { assertLayersEnd { - this.notExists(DOCKED_STACK_DIVIDER) + this.notContains(DOCKED_STACK_DIVIDER) } } fun FlickerTestParameter.appPairsPrimaryBoundsIsVisible(rotation: Int, primaryLayerName: String) { assertLayersEnd { val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER) - this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation)) + this.coversExactly(getPrimaryRegion(dividerRegion, rotation), primaryLayerName) } } @@ -85,7 +83,7 @@ fun FlickerTestParameter.dockedStackPrimaryBoundsIsVisible( ) { assertLayersEnd { val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER) - this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation)) + this.coversExactly(getPrimaryRegion(dividerRegion, rotation), primaryLayerName) } } @@ -95,7 +93,7 @@ fun FlickerTestParameter.appPairsSecondaryBoundsIsVisible( ) { assertLayersEnd { val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER) - this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation)) + this.coversExactly(getSecondaryRegion(dividerRegion, rotation), secondaryLayerName) } } @@ -105,7 +103,7 @@ fun FlickerTestParameter.dockedStackSecondaryBoundsIsVisible( ) { assertLayersEnd { val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER) - this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation)) + this.coversExactly(getSecondaryRegion(dividerRegion, rotation), secondaryLayerName) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt index d2cfb0fbb5f6..03b93c74233c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt @@ -18,3 +18,5 @@ package com.android.wm.shell.flicker const val IME_WINDOW_NAME = "InputMethod" const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui" +const val APP_PAIR_SPLIT_DIVIDER = "AppPairSplitDivider" +const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
\ No newline at end of file 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 5d51b2fd515f..90e71373b1fd 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 @@ -16,7 +16,6 @@ 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 @@ -48,7 +47,7 @@ class AppPairsTestCannotPairNonResizeableApps( testSpec: FlickerTestParameter ) : AppPairsTransition(testSpec) { - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { super.transition(this, it) transitions { 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 77890ba8ed15..dc51b4fb5a9e 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 @@ -16,17 +16,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.FlakyTest import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER 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.traces.layers.getVisibleBounds +import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER import com.android.wm.shell.flicker.appPairsDividerIsVisible import com.android.wm.shell.flicker.helpers.AppPairsHelper import org.junit.FixMethodOrder @@ -46,7 +45,7 @@ import org.junit.runners.Parameterized class AppPairsTestPairPrimaryAndSecondaryApps( testSpec: FlickerTestParameter ) : AppPairsTransition(testSpec) { - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { super.transition(this, it) transitions { @@ -75,10 +74,10 @@ class AppPairsTestPairPrimaryAndSecondaryApps( 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)) + this.coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion), + primaryApp.defaultWindowName) + .coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion), + secondaryApp.defaultWindowName) } } 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 3d3ca0cfd450..5bb9b2f8b8ca 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 @@ -16,17 +16,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.FlakyTest import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER 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.traces.layers.getVisibleBounds +import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER import com.android.wm.shell.flicker.appPairsDividerIsInvisible import com.android.wm.shell.flicker.helpers.AppPairsHelper import org.junit.FixMethodOrder @@ -46,7 +45,7 @@ import org.junit.runners.Parameterized class AppPairsTestUnpairPrimaryAndSecondaryApps( testSpec: FlickerTestParameter ) : AppPairsTransition(testSpec) { - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { super.transition(this, it) setup { @@ -80,10 +79,10 @@ class AppPairsTestUnpairPrimaryAndSecondaryApps( fun appsStartingBounds() { testSpec.assertLayersStart { val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER) - hasVisibleRegion(primaryApp.defaultWindowName, - appPairsHelper.getPrimaryBounds(dividerRegion)) - hasVisibleRegion(secondaryApp.defaultWindowName, - appPairsHelper.getSecondaryBounds(dividerRegion)) + coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion), + primaryApp.defaultWindowName) + coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion), + secondaryApp.defaultWindowName) } } @@ -91,8 +90,8 @@ class AppPairsTestUnpairPrimaryAndSecondaryApps( @Test fun appsEndingBounds() { testSpec.assertLayersEnd { - notExists(primaryApp.defaultWindowName) - notExists(secondaryApp.defaultWindowName) + notContains(primaryApp.defaultWindowName) + notContains(secondaryApp.defaultWindowName) } } 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 9e6752db224f..91e080f65550 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt @@ -17,7 +17,6 @@ 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 @@ -66,7 +65,7 @@ abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter) } } - internal open val transition: FlickerBuilder.(Bundle) -> Unit + internal open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> setup { test { 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 35a0020b16a9..5f003ba62b2d 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 @@ -16,7 +16,6 @@ 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 @@ -52,7 +51,7 @@ import org.junit.runners.Parameterized class RotateTwoLaunchedAppsInAppPairsMode( testSpec: FlickerTestParameter ) : RotateTwoLaunchedAppsTransition(testSpec) { - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { super.transition(this, it) transitions { @@ -72,16 +71,16 @@ class RotateTwoLaunchedAppsInAppPairsMode( } } - @FlakyTest + @Presubmit @Test fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible() - @FlakyTest + @Presubmit @Test fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation) - @FlakyTest + @Presubmit @Test fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation) 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 326a775acc8d..d4792088ac31 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 @@ -16,7 +16,6 @@ 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 @@ -55,7 +54,7 @@ import org.junit.runners.Parameterized class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode( testSpec: FlickerTestParameter ) : RotateTwoLaunchedAppsTransition(testSpec) { - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { super.transition(this, it) transitions { @@ -77,17 +76,8 @@ class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode( @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) - } + fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales( + Surface.ROTATION_0, testSpec.config.endRotation) @Presubmit @Test 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 271b25fc0ce1..83853e61ab5e 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,7 +16,6 @@ package com.android.wm.shell.flicker.apppairs -import android.os.Bundle import android.view.Surface import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.dsl.FlickerBuilder @@ -30,7 +29,7 @@ abstract class RotateTwoLaunchedAppsTransition( override val nonResizeableApp: SplitScreenHelper? get() = null - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { setup { test { 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 9b70fac737e6..17c51fb15b0c 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 @@ -16,7 +16,6 @@ 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 @@ -50,11 +49,10 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -// @FlakyTest(bugId = 179116910) class EnterSplitScreenDockActivity( testSpec: FlickerTestParameter ) : LegacySplitScreenTransition(testSpec) { - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> super.transition(this, configuration) transitions { 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 bd57a59ea3d9..a94fd463c624 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 @@ -16,7 +16,6 @@ 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 @@ -54,7 +53,7 @@ import org.junit.runners.Parameterized class EnterSplitScreenLaunchToSide( testSpec: FlickerTestParameter ) : LegacySplitScreenTransition(testSpec) { - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> super.transition(this, configuration) transitions { 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 67578b29a36c..238059b484b5 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 @@ -16,7 +16,6 @@ package com.android.wm.shell.flicker.legacysplitscreen -import android.os.Bundle import android.view.Surface import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice @@ -50,7 +49,7 @@ import org.junit.runners.Parameterized class EnterSplitScreenNonResizableNotDock( testSpec: FlickerTestParameter ) : LegacySplitScreenTransition(testSpec) { - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> super.transition(this, configuration) teardown { 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 5d42a4a8fae0..acd570a3773e 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 @@ -16,12 +16,10 @@ 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 com.android.server.wm.flicker.DOCKED_STACK_DIVIDER import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory @@ -34,6 +32,7 @@ import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder import org.junit.Test @@ -52,7 +51,7 @@ import org.junit.runners.Parameterized class ExitLegacySplitScreenFromBottom( testSpec: FlickerTestParameter ) : LegacySplitScreenTransition(testSpec) { - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> super.transition(this, configuration) setup { 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 ff8f9c6ed865..cef188695ce7 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 @@ -16,7 +16,6 @@ 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 @@ -53,7 +52,7 @@ import org.junit.runners.Parameterized class ExitPrimarySplitScreenShowSecondaryFullscreen( testSpec: FlickerTestParameter ) : LegacySplitScreenTransition(testSpec) { - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> super.transition(this, configuration) teardown { 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 index 893b101d0759..1e89a25c06df 100644 --- 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 @@ -16,7 +16,6 @@ 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 @@ -27,7 +26,7 @@ import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen abstract class LegacySplitScreenRotateTransition( testSpec: FlickerTestParameter ) : LegacySplitScreenTransition(testSpec) { - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { setup { eachRun { 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 09a7e31d20e2..7f69a66e6e82 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,7 +16,6 @@ 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 @@ -66,7 +65,7 @@ class LegacySplitScreenToLauncher( .launcherStrategy.supportedLauncherPackage private val testApp = SimpleAppHelper(instrumentation) - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> setup { test { 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 6ab1f0bfdb89..91ea8716e4f0 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 @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.app.Instrumentation -import android.os.Bundle import android.support.test.launcherhelper.LauncherStrategyFactory import android.view.Surface import androidx.test.platform.app.InstrumentationRegistry @@ -41,7 +40,7 @@ abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestPa protected val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation) .launcherStrategy.supportedLauncherPackage - protected open val transition: FlickerBuilder.(Bundle) -> Unit + protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> setup { eachRun { @@ -70,7 +69,7 @@ abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestPa } } - internal open val cleanSetup: FlickerBuilder.(Bundle) -> Unit + internal open val cleanSetup: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> setup { eachRun { 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 1b4b54a74eab..caafa278d297 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 @@ -16,12 +16,10 @@ 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 com.android.server.wm.flicker.DOCKED_STACK_DIVIDER import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory @@ -34,6 +32,7 @@ import com.android.server.wm.flicker.layerBecomesInvisible import com.android.server.wm.flicker.layerBecomesVisible import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder import org.junit.Test @@ -53,7 +52,7 @@ import org.junit.runners.Parameterized class NonResizableDismissInLegacySplitScreen( testSpec: FlickerTestParameter ) : LegacySplitScreenTransition(testSpec) { - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> cleanSetup(this, configuration) setup { 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 2365e3bfd8c3..543484ac9759 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 @@ -16,12 +16,10 @@ 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 com.android.server.wm.flicker.DOCKED_STACK_DIVIDER import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory @@ -33,6 +31,7 @@ import com.android.server.wm.flicker.layerBecomesInvisible import com.android.server.wm.flicker.layerBecomesVisible import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder import org.junit.Test @@ -53,7 +52,7 @@ import org.junit.runners.Parameterized class NonResizableLaunchInLegacySplitScreen( testSpec: FlickerTestParameter ) : LegacySplitScreenTransition(testSpec) { - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> cleanSetup(this, configuration) setup { 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 b369a3d32e64..d22833784bcf 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 @@ -16,7 +16,6 @@ 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 @@ -53,7 +52,7 @@ import org.junit.runners.Parameterized class OpenAppToLegacySplitScreen( testSpec: FlickerTestParameter ) : LegacySplitScreenTransition(testSpec) { - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> super.transition(this, configuration) transitions { 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 ffffa1902976..f5174bc3cef7 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 @@ -17,13 +17,11 @@ package com.android.wm.shell.flicker.legacysplitscreen 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.uiautomator.By -import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory @@ -46,6 +44,7 @@ import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEn import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.traces.layers.getVisibleBounds +import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER import com.android.wm.shell.flicker.helpers.SimpleAppHelper import org.junit.FixMethodOrder import org.junit.Test @@ -70,7 +69,7 @@ class ResizeLegacySplitScreen( private val testAppTop = SimpleAppHelper(instrumentation) private val testAppBottom = ImeAppHelper(instrumentation) - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> setup { eachRun { @@ -151,21 +150,21 @@ class ResizeLegacySplitScreen( @Test fun topAppLayerIsAlwaysVisible() { testSpec.assertLayers { - this.showsLayer(sSimpleActivity) + this.isVisible(sSimpleActivity) } } @Test fun bottomAppLayerIsAlwaysVisible() { testSpec.assertLayers { - this.showsLayer(sImeActivity) + this.isVisible(sImeActivity) } } @Test fun dividerLayerIsAlwaysVisible() { testSpec.assertLayers { - this.showsLayer(DOCKED_STACK_DIVIDER) + this.isVisible(DOCKED_STACK_DIVIDER) } } @@ -183,8 +182,8 @@ class ResizeLegacySplitScreen( dividerBounds.bottom - WindowUtils.dockedStackDividerInset, displayBounds.right, displayBounds.bottom - WindowUtils.navigationBarHeight) - this.hasVisibleRegion("SimpleActivity", topAppBounds) - .hasVisibleRegion("ImeActivity", bottomAppBounds) + this.coversExactly(topAppBounds, "SimpleActivity") + .coversExactly(bottomAppBounds, "ImeActivity") } } @@ -203,8 +202,8 @@ class ResizeLegacySplitScreen( displayBounds.right, displayBounds.bottom - WindowUtils.navigationBarHeight) - this.hasVisibleRegion(sSimpleActivity, topAppBounds) - .hasVisibleRegion(sImeActivity, bottomAppBounds) + this.coversExactly(topAppBounds, sSimpleActivity) + .coversExactly(bottomAppBounds, sImeActivity) } } 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 c538008aa179..c914adae2b7c 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 @@ -16,7 +16,6 @@ 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 @@ -54,7 +53,7 @@ import org.junit.runners.Parameterized class RotateOneLaunchedAppAndEnterSplitScreen( testSpec: FlickerTestParameter ) : LegacySplitScreenRotateTransition(testSpec) { - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> super.transition(this, configuration) transitions { 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 c1162560119c..ffb20a4bc99a 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 @@ -16,7 +16,6 @@ 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 @@ -54,7 +53,7 @@ import org.junit.runners.Parameterized class RotateOneLaunchedAppInSplitScreenMode( testSpec: FlickerTestParameter ) : LegacySplitScreenRotateTransition(testSpec) { - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> super.transition(this, configuration) transitions { 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 273925c0361c..8cf1990d406f 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 @@ -16,7 +16,6 @@ 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 @@ -56,7 +55,7 @@ import org.junit.runners.Parameterized class RotateTwoLaunchedAppAndEnterSplitScreen( testSpec: FlickerTestParameter ) : LegacySplitScreenRotateTransition(testSpec) { - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> super.transition(this, configuration) transitions { 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 c7188dc227e7..9c798d8ea661 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 @@ -16,7 +16,6 @@ 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 @@ -56,7 +55,7 @@ import org.junit.runners.Parameterized class RotateTwoLaunchedAppInSplitScreenMode( testSpec: FlickerTestParameter ) : LegacySplitScreenRotateTransition(testSpec) { - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> super.transition(this, configuration) setup { 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 ca48eaa45840..75c33c671008 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,7 +16,6 @@ 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 @@ -51,7 +50,7 @@ class EnterExitPipTest( private val testApp = FixedAppHelper(instrumentation) private val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation) - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = buildTransition(eachRun = true) { setup { eachRun { @@ -68,7 +67,7 @@ class EnterExitPipTest( @Test fun pipAppRemainInsideVisibleBounds() { testSpec.assertWm { - coversAtMostRegion(pipApp.defaultWindowName, displayBounds) + coversAtMost(displayBounds, pipApp.defaultWindowName) } } @@ -95,10 +94,10 @@ class EnterExitPipTest( @Test fun showBothAppLayersThenHidePip() { testSpec.assertLayers { - showsLayer(testApp.defaultWindowName) - .showsLayer(pipApp.defaultWindowName) + isVisible(testApp.defaultWindowName) + .isVisible(pipApp.defaultWindowName) .then() - .hidesLayer(testApp.defaultWindowName) + .isInvisible(testApp.defaultWindowName) } } @@ -106,8 +105,8 @@ class EnterExitPipTest( @Test fun testAppCoversFullScreenWithPipOnDisplay() { testSpec.assertLayersStart { - hasVisibleRegion(testApp.defaultWindowName, displayBounds) - coversAtMostRegion(displayBounds, pipApp.defaultWindowName) + coversExactly(displayBounds, testApp.defaultWindowName) + coversAtMost(displayBounds, pipApp.defaultWindowName) } } @@ -115,7 +114,7 @@ class EnterExitPipTest( @Test fun pipAppCoversFullScreen() { testSpec.assertLayersEnd { - hasVisibleRegion(pipApp.defaultWindowName, displayBounds) + coversExactly(displayBounds, pipApp.defaultWindowName) } } 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 e1fbc16e8ce2..83dca53b1542 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,7 +16,6 @@ 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 @@ -48,7 +47,7 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = buildTransition(eachRun = true, stringExtras = emptyMap()) { transitions { pipApp.clickEnterPipButton() @@ -85,7 +84,7 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { @Test fun pipLayerBecomesVisible() { testSpec.assertLayers { - this.showsLayer(pipApp.launcherName) + this.isVisible(pipApp.launcherName) } } 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 215b97bfeb83..9011f1a9fb6a 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,10 +16,8 @@ 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 com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter @@ -56,7 +54,7 @@ class EnterPipToOtherOrientationTest( private val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90) private val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0) - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> setupAndTeardown(this, configuration) @@ -121,24 +119,24 @@ class EnterPipToOtherOrientationTest( @Test fun pipAppLayerHidesTestApp() { testSpec.assertLayersStart { - hasVisibleRegion(pipApp.defaultWindowName, startingBounds) + coversExactly(startingBounds, pipApp.defaultWindowName) isInvisible(testApp.defaultWindowName) } } - @FlakyTest + @Presubmit @Test fun testAppLayerCoversFullScreen() { testSpec.assertLayersEnd { - hasVisibleRegion(testApp.defaultWindowName, endingBounds) + coversExactly(endingBounds, testApp.defaultWindowName) } } - @FlakyTest(bugId = 140855415) + @Presubmit @Test fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - @FlakyTest(bugId = 140855415) + @Presubmit @Test fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt index 707d28d9c4c0..96eb66c3cc28 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt @@ -48,7 +48,7 @@ fun WindowManagerStateSubject.isInPipMode( activity: ComponentName ): WindowManagerStateSubject = apply { val windowName = activity.toWindowName() - hasWindow(windowName) + contains(windowName) val pinnedWindows = wmState.pinnedWindows .map { it.title } Truth.assertWithMessage("Window not in PIP mode") 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 f3b9ea1455ca..46339603f806 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,7 +16,6 @@ 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 @@ -46,7 +45,7 @@ import org.junit.runners.Parameterized class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { private val imeApp = ImeAppHelper(instrumentation) - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = buildTransition(eachRun = false) { configuration -> setup { test { @@ -78,7 +77,7 @@ class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) fun pipInVisibleBounds() { testSpec.assertWm { val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation) - coversAtMostRegion(pipApp.defaultWindowName, displayBounds) + coversAtMost(displayBounds, pipApp.defaultWindowName) } } 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 daf381ee9ddb..97afc65b8b61 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt @@ -16,7 +16,6 @@ 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 @@ -58,7 +57,7 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t private val testApp = FixedAppHelper(instrumentation) private val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation) - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { withTestName { testSpec.name } repeat { testSpec.config.repetitions } @@ -90,7 +89,7 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t @Test fun pipWindowInsideDisplayBounds() { testSpec.assertWm { - coversAtMostRegion(pipApp.defaultWindowName, displayBounds) + coversAtMost(displayBounds, pipApp.defaultWindowName) } } @@ -100,7 +99,7 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t testSpec.assertWmEnd { isVisible(testApp.defaultWindowName) isVisible(imeApp.defaultWindowName) - noWindowsOverlap(setOf(testApp.defaultWindowName, imeApp.defaultWindowName)) + noWindowsOverlap(testApp.defaultWindowName, imeApp.defaultWindowName) } } @@ -116,7 +115,7 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t @Test fun pipLayerInsideDisplayBounds() { testSpec.assertLayers { - coversAtMostRegion(displayBounds, pipApp.defaultWindowName) + coversAtMost(displayBounds, pipApp.defaultWindowName) } } @@ -124,8 +123,8 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t @Test fun bothAppLayersVisible() { testSpec.assertLayersEnd { - coversAtMostRegion(displayBounds, testApp.defaultWindowName) - coversAtMostRegion(displayBounds, imeApp.defaultWindowName) + coversAtMost(displayBounds, testApp.defaultWindowName) + coversAtMost(displayBounds, imeApp.defaultWindowName) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt new file mode 100644 index 000000000000..4c95da284d9e --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt @@ -0,0 +1,94 @@ +/* + * 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.pip + +import android.platform.test.annotations.Postsubmit +import android.view.Surface +import androidx.test.filters.RequiresDevice +import com.android.launcher3.tapl.LauncherInstrumentation +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.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.google.common.truth.Truth +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test Pip launch. + * To run this test: `atest WMShellFlickerTests:PipMovesInAllApps` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class PipMovesInAllApps(testSpec: FlickerTestParameter) : PipTransition(testSpec) { + private val taplInstrumentation = LauncherInstrumentation() + + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = buildTransition(eachRun = false) { + teardown { + eachRun { + taplInstrumentation.pressHome() + } + } + transitions { + taplInstrumentation.pressHome().switchToAllApps() + wmHelper.waitForAppTransitionIdle() + } + } + + @Postsubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + fun pipAlwaysVisible() = testSpec.assertWm { this.showsAppWindow(pipApp.windowName) } + + @Postsubmit + @Test + fun pipWindowMovesUp() = testSpec.assertWmEnd { + val initialState = this.trace?.first()?.wmState + ?: error("Trace should not be empty") + val startPos = initialState.pinnedWindows.first().frame + val currPos = this.wmState.pinnedWindows.first().frame + val subject = Truth.assertWithMessage("Pip should have moved up") + subject.that(currPos.top).isGreaterThan(startPos.top) + subject.that(currPos.bottom).isGreaterThan(startPos.bottom) + subject.that(currPos.left).isEqualTo(startPos.left) + subject.that(currPos.right).isEqualTo(startPos.right) + } + + 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/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt index 43c12acef9e8..df835d21e73f 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,7 +16,6 @@ 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 @@ -56,7 +55,7 @@ class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) private val startingBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation) private val endingBounds = WindowUtils.getDisplayBounds(testSpec.config.endRotation) - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = buildTransition(eachRun = false) { configuration -> setup { test { @@ -89,11 +88,11 @@ class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, testSpec.config.endRotation, allStates = false) - @FlakyTest(bugId = 140855415) + @Presubmit @Test fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - @FlakyTest(bugId = 140855415) + @Presubmit @Test fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() @@ -113,8 +112,8 @@ class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) @Test fun appLayerRotates_StartingBounds() { testSpec.assertLayersStart { - hasVisibleRegion(fixedApp.defaultWindowName, startingBounds) - coversAtMostRegion(startingBounds, pipApp.defaultWindowName) + coversExactly(startingBounds, fixedApp.defaultWindowName) + coversAtMost(startingBounds, pipApp.defaultWindowName) } } @@ -122,8 +121,8 @@ class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) @Test fun appLayerRotates_EndingBounds() { testSpec.assertLayersEnd { - hasVisibleRegion(fixedApp.defaultWindowName, endingBounds) - coversAtMostRegion(endingBounds, pipApp.defaultWindowName) + coversExactly(endingBounds, fixedApp.defaultWindowName) + coversAtMost(endingBounds, pipApp.defaultWindowName) } } 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 02389a9ccf87..1bb1d2861f3f 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,7 +16,6 @@ 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 @@ -50,7 +49,7 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = buildTransition(eachRun = true) { configuration -> setup { eachRun { @@ -67,7 +66,7 @@ class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { } } - @FlakyTest(bugId = 140855415) + @Presubmit @Test fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() @@ -102,18 +101,18 @@ class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { @Test fun appReplacesPipLayer() { testSpec.assertLayers { - this.showsLayer(PIP_WINDOW_TITLE) + this.isVisible(PIP_WINDOW_TITLE) .then() - .showsLayer(pipApp.launcherName) + .isVisible(pipApp.launcherName) } } - @FlakyTest + @Presubmit @Test fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0) - @FlakyTest(bugId = 140855415) + @Presubmit @Test fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) 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 968a11de2511..92928b16ee1a 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,8 +16,6 @@ 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 @@ -29,6 +27,7 @@ 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 @@ -50,7 +49,7 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class PipToHomeTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = buildTransition(eachRun = true) { configuration -> setup { eachRun { @@ -67,7 +66,7 @@ class PipToHomeTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { } } - @Postsubmit + @Presubmit @Test fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() @@ -97,9 +96,9 @@ class PipToHomeTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { @Test fun pipLayerBecomesInvisible() { testSpec.assertLayers { - this.showsLayer(PIP_WINDOW_TITLE) + this.isVisible(PIP_WINDOW_TITLE) .then() - .hidesLayer(PIP_WINDOW_TITLE) + .isInvisible(PIP_WINDOW_TITLE) } } @@ -108,15 +107,15 @@ class PipToHomeTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) - @Postsubmit + @Presubmit @Test fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0) - @Postsubmit + @Presubmit @Test fun navBarLayerRotatesAndScales() = - testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) @FlakyTest(bugId = 151179149) @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt index a94483ec00a0..b0a9afef9215 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt @@ -18,7 +18,6 @@ package com.android.wm.shell.flicker.pip 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 @@ -38,7 +37,7 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) { protected val isRotated = testSpec.config.startRotation.isRotated() protected val pipApp = PipAppHelper(instrumentation) protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation) - protected abstract val transition: FlickerBuilder.(Bundle) -> Unit + protected abstract val transition: FlickerBuilder.(Map<String, Any?>) -> Unit // Helper class to process test actions by broadcast. protected class BroadcastActionTrigger(private val instrumentation: Instrumentation) { @@ -81,7 +80,7 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) { /** * Gets a configuration that handles basic setup and teardown of pip tests */ - protected val setupAndTeardown: FlickerBuilder.(Bundle) -> Unit + protected val setupAndTeardown: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { setup { test { @@ -112,8 +111,8 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) { protected open fun buildTransition( eachRun: Boolean, stringExtras: Map<String, String> = mapOf(Components.PipActivity.EXTRA_ENTER_PIP to "true"), - extraSpec: FlickerBuilder.(Bundle) -> Unit = {} - ): FlickerBuilder.(Bundle) -> Unit { + extraSpec: FlickerBuilder.(Map<String, Any?>) -> Unit = {} + ): FlickerBuilder.(Map<String, Any?>) -> Unit { return { configuration -> setupAndTeardown(this, configuration) 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 1f0370dc0505..7916ce59af52 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,7 +16,6 @@ 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 @@ -54,7 +53,7 @@ class SetRequestedOrientationWhilePinnedTest( private val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0) private val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90) - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> setupAndTeardown(this, configuration) @@ -88,7 +87,7 @@ class SetRequestedOrientationWhilePinnedTest( @Test fun pipWindowInsideDisplay() { testSpec.assertWmStart { - coversAtMostRegion(pipApp.defaultWindowName, startingBounds) + coversAtMost(startingBounds, pipApp.defaultWindowName) } } @@ -112,7 +111,7 @@ class SetRequestedOrientationWhilePinnedTest( @Test fun pipLayerInsideDisplay() { testSpec.assertLayersStart { - coversAtMostRegion(startingBounds, pipApp.defaultWindowName) + coversAtMost(startingBounds, pipApp.defaultWindowName) } } @@ -120,15 +119,15 @@ class SetRequestedOrientationWhilePinnedTest( @Test fun pipAppLayerCoversFullScreen() { testSpec.assertLayersEnd { - hasVisibleRegion(pipApp.defaultWindowName, endingBounds) + coversExactly(endingBounds, pipApp.defaultWindowName) } } - @FlakyTest(bugId = 140855415) + @Presubmit @Test fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - @FlakyTest(bugId = 140855415) + @Presubmit @Test fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() 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/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/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 b570af5a0c93..4eefe921fbe9 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.cpp @@ -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/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/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/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index 5e3966032a11..38b48e97771a 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -117,7 +117,8 @@ interface ILocationManager boolean isLocationEnabledForUser(int userId); void setLocationEnabledForUser(boolean enabled, int userId); - void addTestProvider(String name, in ProviderProperties properties, String packageName, @nullable String attributionTag); + void addTestProvider(String name, in ProviderProperties properties, + in List<String> locationTags, String packageName, @nullable String attributionTag); void removeTestProvider(String provider, String packageName, @nullable String attributionTag); void setTestProviderLocation(String provider, in Location location, String packageName, @nullable String attributionTag); void setTestProviderEnabled(String provider, boolean enabled, String packageName, @nullable String attributionTag); diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index dd5b6e6b4222..95bae5ae7aab 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -76,6 +76,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -1957,12 +1958,32 @@ public class LocationManager { * allowed} for your app. */ public void addTestProvider(@NonNull String provider, @NonNull ProviderProperties properties) { + addTestProvider(provider, properties, Collections.emptySet()); + } + + /** + * Creates a test location provider and adds it to the set of active providers. This provider + * will replace any provider with the same name that exists prior to this call. + * + * @param provider the provider name + * @param properties the provider properties + * @param locationTags the attribution tags for accessing location from the provider + * + * @throws IllegalArgumentException if provider is null + * @throws IllegalArgumentException if properties is null + * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION + * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED + * allowed} for your app. + */ + public void addTestProvider(@NonNull String provider, @NonNull ProviderProperties properties, + @NonNull Set<String> locationTags) { Preconditions.checkArgument(provider != null, "invalid null provider"); Preconditions.checkArgument(properties != null, "invalid null properties"); + Preconditions.checkArgument(locationTags != null, "invalid null location tags"); try { - mService.addTestProvider(provider, properties, mContext.getOpPackageName(), - mContext.getFeatureId()); + mService.addTestProvider(provider, properties, new ArrayList<>(locationTags), + mContext.getOpPackageName(), mContext.getFeatureId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/location/java/android/location/LocationManagerInternal.java b/location/java/android/location/LocationManagerInternal.java index a6a0e7aa24ff..763835c9cbe2 100644 --- a/location/java/android/location/LocationManagerInternal.java +++ b/location/java/android/location/LocationManagerInternal.java @@ -21,6 +21,10 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.location.util.identity.CallerIdentity; +import com.android.internal.annotations.Immutable; + +import java.util.Set; + /** * Location manager local system service interface. * @@ -39,6 +43,21 @@ public abstract class LocationManagerInternal { } /** + * Interface for getting callbacks when a location provider's location tags change. + * + * @see LocationTagInfo + */ + public interface OnProviderLocationTagsChangeListener { + + /** + * Called when the location tags for a provider change. + * + * @param providerLocationTagInfo The tag info for a provider. + */ + void onLocationTagsChanged(@NonNull LocationTagInfo providerLocationTagInfo); + } + + /** * Returns true if the given provider is enabled for the given user. * * @param provider A location provider as listed by {@link LocationManager#getAllProviders()} @@ -88,4 +107,60 @@ public abstract class LocationManagerInternal { * provider, and the elapsed nanos since boot the current time was computed at. */ public abstract @Nullable LocationTime getGnssTimeMillis(); + + /** + * Sets a listener for changes in the location providers' tags. Passing + * {@code null} clears the current listener. + * + * @param listener The listener. + */ + public abstract void setOnProviderLocationTagsChangeListener( + @Nullable OnProviderLocationTagsChangeListener listener); + + /** + * This class represents the location permission tags used by the location provider + * packages in a given UID. These tags are strictly used for accessing state guarded + * by the location permission(s) by a location provider which are required for the + * provider to fulfill its function as being a location provider. + */ + @Immutable + public static class LocationTagInfo { + private final int mUid; + + @NonNull + private final String mPackageName; + + @Nullable + private final Set<String> mLocationTags; + + public LocationTagInfo(int uid, @NonNull String packageName, + @Nullable Set<String> locationTags) { + mUid = uid; + mPackageName = packageName; + mLocationTags = locationTags; + } + + /** + * @return The UID for which tags are related. + */ + public int getUid() { + return mUid; + } + + /** + * @return The package for which tags are related. + */ + @NonNull + public String getPackageName() { + return mPackageName; + } + + /** + * @return The tags for the package used for location related accesses. + */ + @Nullable + public Set<String> getTags() { + return mLocationTags; + } + } } diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index f87f90d8e0a2..b2de49deefca 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -568,7 +568,7 @@ public class AudioManager { public static final int FLAG_FROM_KEY = 1 << 12; /** @hide */ - @IntDef(flag = false, prefix = "FLAG", value = { + @IntDef(flag = true, prefix = "FLAG", value = { FLAG_SHOW_UI, FLAG_ALLOW_RINGER_MODES, FLAG_PLAY_SOUND, diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 6fcb75616f0d..58174a0cf408 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -3526,8 +3526,9 @@ public class AudioTrack extends PlayerBase native_enableDeviceCallback(); return true; } catch (IllegalStateException e) { - // Fail silently as track state could have changed in between start - // and enabling routing callback, return false to indicate not enabled + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "testEnableNativeRoutingCallbacks failed", e); + } } } return false; @@ -3577,7 +3578,7 @@ public class AudioTrack extends PlayerBase Handler handler) { synchronized (mRoutingChangeListeners) { if (listener != null && !mRoutingChangeListeners.containsKey(listener)) { - testEnableNativeRoutingCallbacksLocked(); + mEnableSelfRoutingMonitor = testEnableNativeRoutingCallbacksLocked(); mRoutingChangeListeners.put( listener, new NativeRoutingEventHandlerDelegate(this, listener, handler != null ? handler : new Handler(mInitializationLooper))); diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index 67f1660fff78..f8a642a68dd7 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -980,6 +980,74 @@ public final class MediaFormat { public static final String KEY_BITRATE_MODE = "bitrate-mode"; /** + * A key describing the maximum Quantization Parameter allowed for encoding video. + * This key applies to all three video frame types (I, P, and B). This value fills + * in for any of the frame-specific #KEY_VIDEO_QP_I_MAX, #KEY_VIDEO_QP_P_MAX, or + * #KEY_VIDEO_QP_B_MAX keys that are not specified + * + * The associated value is an integer. + */ + public static final String KEY_VIDEO_QP_MAX = "video-qp-max"; + + /** + * A key describing the maximum Quantization Parameter allowed for encoding video. + * This key applies to all three video frame types (I, P, and B). This value fills + * in for any of the frame-specific #KEY_VIDEO_QP_I_MIN, #KEY_VIDEO_QP_P_MIN, or + * #KEY_VIDEO_QP_B_MIN keys that are not specified + * + * The associated value is an integer. + */ + public static final String KEY_VIDEO_QP_MIN = "video-qp-min"; + + /** + * A key describing the maximum Quantization Parameter allowed for encoding video. + * This value applies to video I-frames. + * + * The associated value is an integer. + */ + public static final String KEY_VIDEO_QP_I_MAX = "video-qp-i-max"; + + /** + * A key describing the minimum Quantization Parameter allowed for encoding video. + * This value applies to video I-frames. + * + * The associated value is an integer. + */ + public static final String KEY_VIDEO_QP_I_MIN = "video-qp-i-min"; + + /** + * A key describing the maximum Quantization Parameter allowed for encoding video. + * This value applies to video P-frames. + * + * The associated value is an integer. + */ + public static final String KEY_VIDEO_QP_P_MAX = "video-qp-p-max"; + + /** + * A key describing the minimum Quantization Parameter allowed for encoding video. + * This value applies to video P-frames. + * + * The associated value is an integer. + */ + public static final String KEY_VIDEO_QP_P_MIN = "video-qp-p-min"; + + /** + * A key describing the maximum Quantization Parameter allowed for encoding video. + * This value applies to video B-frames. + * + * The associated value is an integer. + */ + public static final String KEY_VIDEO_QP_B_MAX = "video-qp-b-max"; + + /** + * A key describing the minimum Quantization Parameter allowed for encoding video. + * This value applies to video B-frames. + * + * The associated value is an integer. + */ + public static final String KEY_VIDEO_QP_B_MIN = "video-qp-b-min"; + + /** * A key describing the audio session ID of the AudioTrack associated * to a tunneled video codec. * The associated value is an integer. diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index f3cee17ab238..9176dae8609f 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -1360,6 +1360,7 @@ public class MediaPlayer extends PlayerBase private void startImpl() { baseStart(0); // unknown device at this point stayAwake(true); + tryToEnableNativeRoutingCallback(); _start(); } @@ -1385,6 +1386,7 @@ public class MediaPlayer extends PlayerBase stayAwake(false); _stop(); baseStop(); + tryToDisableNativeRoutingCallback(); } private native void _stop() throws IllegalStateException; @@ -1526,8 +1528,9 @@ public class MediaPlayer extends PlayerBase native_enableDeviceCallback(true); return true; } catch (IllegalStateException e) { - // Fail silently as media player state could have changed in between start - // and enabling routing callback, return false to indicate not enabled + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "testEnableNativeRoutingCallbacks failed", e); + } } } return false; @@ -1590,7 +1593,7 @@ public class MediaPlayer extends PlayerBase Handler handler) { synchronized (mRoutingChangeListeners) { if (listener != null && !mRoutingChangeListeners.containsKey(listener)) { - testEnableNativeRoutingCallbacksLocked(); + mEnableSelfRoutingMonitor = testEnableNativeRoutingCallbacksLocked(); mRoutingChangeListeners.put( listener, new NativeRoutingEventHandlerDelegate(this, listener, handler != null ? handler : mEventHandler)); @@ -3483,9 +3486,6 @@ public class MediaPlayer extends PlayerBase case MEDIA_STOPPED: { - tryToDisableNativeRoutingCallback(); - // FIXME see b/179218630 - //baseStop(); TimeProvider timeProvider = mTimeProvider; if (timeProvider != null) { timeProvider.onStopped(); @@ -3494,18 +3494,9 @@ public class MediaPlayer extends PlayerBase break; case MEDIA_STARTED: - { - // FIXME see b/179218630 - //baseStart(native_getRoutedDeviceId()); - tryToEnableNativeRoutingCallback(); - } // fall through case MEDIA_PAUSED: { - // FIXME see b/179218630 - //if (msg.what == MEDIA_PAUSED) { - // basePause(); - //} TimeProvider timeProvider = mTimeProvider; if (timeProvider != null) { timeProvider.onPaused(msg.what == MEDIA_PAUSED); diff --git a/media/java/android/mtp/MtpServer.java b/media/java/android/mtp/MtpServer.java index 042d02fa3d48..1d2657a280a2 100644 --- a/media/java/android/mtp/MtpServer.java +++ b/media/java/android/mtp/MtpServer.java @@ -144,11 +144,6 @@ public class MtpServer implements Runnable { native_remove_storage(storage.getStorageId()); } - public static void configure(boolean usePtp) { - native_configure(usePtp); - } - - public static native final void native_configure(boolean usePtp); private native final void native_setup( MtpDatabase database, FileDescriptor controlFd, diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 3669bf44c42a..b7cde57beaf5 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -561,7 +561,7 @@ jobjectArray FilterClientCallbackImpl::getTsRecordEvent( const std::vector<DemuxFilterEventExt::Event>& eventsExt) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/TsRecordEvent"); - jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIJJ)V"); + jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIJJI)V"); for (int i = 0; i < events.size(); i++) { auto event = events[i]; @@ -614,7 +614,7 @@ jobjectArray FilterClientCallbackImpl::getMmtpRecordEvent( const std::vector<DemuxFilterEventExt::Event>& eventsExt) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/MmtpRecordEvent"); - jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IJIJ)V"); + jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IJIJII)V"); for (int i = 0; i < events.size(); i++) { auto event = events[i]; @@ -3571,26 +3571,28 @@ static DemuxFilterSettings getFilterConfiguration( .tpid = tpid, }; - DemuxTsFilterType tsType = static_cast<DemuxTsFilterType>(subtype); - switch (tsType) { - case DemuxTsFilterType::SECTION: - tsFilterSettings.filterSettings.section( - getFilterSectionSettings(env, settingsObj)); - break; - case DemuxTsFilterType::AUDIO: - case DemuxTsFilterType::VIDEO: - tsFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj)); - break; - case DemuxTsFilterType::PES: - tsFilterSettings.filterSettings.pesData( - getFilterPesDataSettings(env, settingsObj)); - break; - case DemuxTsFilterType::RECORD: - tsFilterSettings.filterSettings.record( - getFilterRecordSettings(env, settingsObj)); - break; - default: - break; + if (settingsObj != NULL) { + DemuxTsFilterType tsType = static_cast<DemuxTsFilterType>(subtype); + switch (tsType) { + case DemuxTsFilterType::SECTION: + tsFilterSettings.filterSettings.section( + getFilterSectionSettings(env, settingsObj)); + break; + case DemuxTsFilterType::AUDIO: + case DemuxTsFilterType::VIDEO: + tsFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj)); + break; + case DemuxTsFilterType::PES: + tsFilterSettings.filterSettings.pesData( + getFilterPesDataSettings(env, settingsObj)); + break; + case DemuxTsFilterType::RECORD: + tsFilterSettings.filterSettings.record( + getFilterRecordSettings(env, settingsObj)); + break; + default: + break; + } } filterSettings.ts(tsFilterSettings); break; @@ -3602,60 +3604,55 @@ static DemuxFilterSettings getFilterConfiguration( DemuxMmtpFilterSettings mmtpFilterSettings { .mmtpPid = mmtpPid, }; - DemuxMmtpFilterType mmtpType = static_cast<DemuxMmtpFilterType>(subtype); - switch (mmtpType) { - case DemuxMmtpFilterType::SECTION: - mmtpFilterSettings.filterSettings.section( - getFilterSectionSettings(env, settingsObj)); - break; - case DemuxMmtpFilterType::AUDIO: - case DemuxMmtpFilterType::VIDEO: - mmtpFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj)); - break; - case DemuxMmtpFilterType::PES: - mmtpFilterSettings.filterSettings.pesData( - getFilterPesDataSettings(env, settingsObj)); - break; - case DemuxMmtpFilterType::RECORD: - mmtpFilterSettings.filterSettings.record( - getFilterRecordSettings(env, settingsObj)); - break; - case DemuxMmtpFilterType::DOWNLOAD: - mmtpFilterSettings.filterSettings.download( - getFilterDownloadSettings(env, settingsObj)); - break; - default: - break; + + if (settingsObj != NULL) { + DemuxMmtpFilterType mmtpType = static_cast<DemuxMmtpFilterType>(subtype); + switch (mmtpType) { + case DemuxMmtpFilterType::SECTION: + mmtpFilterSettings.filterSettings.section( + getFilterSectionSettings(env, settingsObj)); + break; + case DemuxMmtpFilterType::AUDIO: + case DemuxMmtpFilterType::VIDEO: + mmtpFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj)); + break; + case DemuxMmtpFilterType::PES: + mmtpFilterSettings.filterSettings.pesData( + getFilterPesDataSettings(env, settingsObj)); + break; + case DemuxMmtpFilterType::RECORD: + mmtpFilterSettings.filterSettings.record( + getFilterRecordSettings(env, settingsObj)); + break; + case DemuxMmtpFilterType::DOWNLOAD: + mmtpFilterSettings.filterSettings.download( + getFilterDownloadSettings(env, settingsObj)); + break; + default: + break; + } } filterSettings.mmtp(mmtpFilterSettings); break; } case DemuxFilterMainType::IP: { DemuxIpAddress ipAddr = getDemuxIpAddress(env, filterConfigObj); - DemuxIpFilterSettings ipFilterSettings { .ipAddr = ipAddr, }; + DemuxIpFilterType ipType = static_cast<DemuxIpFilterType>(subtype); - switch (ipType) { - case DemuxIpFilterType::SECTION: { - ipFilterSettings.filterSettings.section( - getFilterSectionSettings(env, settingsObj)); - break; - } - case DemuxIpFilterType::IP: { - jclass clazz = env->FindClass( - "android/media/tv/tuner/filter/IpFilterConfiguration"); - bool bPassthrough = static_cast<bool>( - env->GetBooleanField( - filterConfigObj, env->GetFieldID( - clazz, "mPassthrough", "Z"))); - ipFilterSettings.filterSettings.bPassthrough(bPassthrough); - break; - } - default: { - break; - } + if (ipType == DemuxIpFilterType::SECTION && settingsObj != NULL) { + ipFilterSettings.filterSettings.section( + getFilterSectionSettings(env, settingsObj)); + } else if (ipType == DemuxIpFilterType::IP) { + jclass clazz = env->FindClass( + "android/media/tv/tuner/filter/IpFilterConfiguration"); + bool bPassthrough = static_cast<bool>( + env->GetBooleanField( + filterConfigObj, env->GetFieldID( + clazz, "mPassthrough", "Z"))); + ipFilterSettings.filterSettings.bPassthrough(bPassthrough); } filterSettings.ip(ipFilterSettings); break; @@ -3672,24 +3669,17 @@ static DemuxFilterSettings getFilterConfiguration( .packetType = packetType, .isCompressedIpPacket = isCompressedIpPacket, }; + DemuxTlvFilterType tlvType = static_cast<DemuxTlvFilterType>(subtype); - switch (tlvType) { - case DemuxTlvFilterType::SECTION: { - tlvFilterSettings.filterSettings.section( - getFilterSectionSettings(env, settingsObj)); - break; - } - case DemuxTlvFilterType::TLV: { - bool bPassthrough = static_cast<bool>( - env->GetBooleanField( - filterConfigObj, env->GetFieldID( - clazz, "mPassthrough", "Z"))); - tlvFilterSettings.filterSettings.bPassthrough(bPassthrough); - break; - } - default: { - break; - } + if (tlvType == DemuxTlvFilterType::SECTION && settingsObj != NULL) { + tlvFilterSettings.filterSettings.section( + getFilterSectionSettings(env, settingsObj)); + } else if (tlvType == DemuxTlvFilterType::TLV) { + bool bPassthrough = static_cast<bool>( + env->GetBooleanField( + filterConfigObj, env->GetFieldID( + clazz, "mPassthrough", "Z"))); + tlvFilterSettings.filterSettings.bPassthrough(bPassthrough); } filterSettings.tlv(tlvFilterSettings); break; @@ -3704,14 +3694,17 @@ static DemuxFilterSettings getFilterConfiguration( .packetType = packetType, .lengthType = lengthType, }; - DemuxAlpFilterType alpType = static_cast<DemuxAlpFilterType>(subtype); - switch (alpType) { - case DemuxAlpFilterType::SECTION: - alpFilterSettings.filterSettings.section( - getFilterSectionSettings(env, settingsObj)); - break; - default: - break; + + if (settingsObj != NULL) { + DemuxAlpFilterType alpType = static_cast<DemuxAlpFilterType>(subtype); + switch (alpType) { + case DemuxAlpFilterType::SECTION: + alpFilterSettings.filterSettings.section( + getFilterSectionSettings(env, settingsObj)); + break; + default: + break; + } } filterSettings.alp(alpFilterSettings); break; 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..f1a98adc0ab2 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.mDevicesAdapter != 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/Android.bp b/packages/Connectivity/framework/Android.bp index c7e261c93689..86b85e8398ce 100644 --- a/packages/Connectivity/framework/Android.bp +++ b/packages/Connectivity/framework/Android.bp @@ -58,3 +58,28 @@ filegroup { "//packages/modules/Connectivity:__subpackages__", ], } + +java_sdk_library { + name: "framework-connectivity", + api_only: true, + defaults: ["framework-module-defaults"], + // TODO: build against module API + platform_apis: true, + srcs: [ + ":framework-connectivity-sources", + ], + aidl: { + include_dirs: [ + // Include directories for parcelables that are part of the stable API, and need a + // one-line "parcelable X" .aidl declaration to be used in AIDL interfaces. + // TODO(b/180293679): remove these dependencies as they should not be necessary once + // the module builds against API (the parcelable declarations exist in framework.aidl) + "frameworks/base/core/java", // For framework parcelables + "frameworks/native/aidl/binder", // For PersistableBundle.aidl + ], + }, + libs: [ + "unsupportedappusage", + ], + permitted_packages: ["android.net", "com.android.connectivity.aidl"], +} diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt new file mode 100644 index 000000000000..31b8fc8ae53a --- /dev/null +++ b/packages/Connectivity/framework/api/current.txt @@ -0,0 +1,470 @@ +// Signature format: 2.0 +package android.net { + + public class CaptivePortal implements android.os.Parcelable { + method public int describeContents(); + method public void ignoreNetwork(); + method public void reportCaptivePortalDismissed(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortal> CREATOR; + } + + public class ConnectivityDiagnosticsManager { + method public void registerConnectivityDiagnosticsCallback(@NonNull android.net.NetworkRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback); + method public void unregisterConnectivityDiagnosticsCallback(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback); + } + + public abstract static class ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback { + ctor public ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback(); + method public void onConnectivityReportAvailable(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityReport); + method public void onDataStallSuspected(@NonNull android.net.ConnectivityDiagnosticsManager.DataStallReport); + method public void onNetworkConnectivityReported(@NonNull android.net.Network, boolean); + } + + public static final class ConnectivityDiagnosticsManager.ConnectivityReport implements android.os.Parcelable { + ctor public ConnectivityDiagnosticsManager.ConnectivityReport(@NonNull android.net.Network, long, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle); + method public int describeContents(); + method @NonNull public android.os.PersistableBundle getAdditionalInfo(); + method @NonNull public android.net.LinkProperties getLinkProperties(); + method @NonNull public android.net.Network getNetwork(); + method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities(); + method public long getReportTimestamp(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.ConnectivityReport> CREATOR; + field public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = "networkProbesAttempted"; + field public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK = "networkProbesSucceeded"; + field public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult"; + field public static final int NETWORK_PROBE_DNS = 4; // 0x4 + field public static final int NETWORK_PROBE_FALLBACK = 32; // 0x20 + field public static final int NETWORK_PROBE_HTTP = 8; // 0x8 + field public static final int NETWORK_PROBE_HTTPS = 16; // 0x10 + field public static final int NETWORK_PROBE_PRIVATE_DNS = 64; // 0x40 + field public static final int NETWORK_VALIDATION_RESULT_INVALID = 0; // 0x0 + field public static final int NETWORK_VALIDATION_RESULT_PARTIALLY_VALID = 2; // 0x2 + field public static final int NETWORK_VALIDATION_RESULT_SKIPPED = 3; // 0x3 + field public static final int NETWORK_VALIDATION_RESULT_VALID = 1; // 0x1 + } + + public static final class ConnectivityDiagnosticsManager.DataStallReport implements android.os.Parcelable { + ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle); + method public int describeContents(); + method public int getDetectionMethod(); + method @NonNull public android.net.LinkProperties getLinkProperties(); + method @NonNull public android.net.Network getNetwork(); + method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities(); + method public long getReportTimestamp(); + method @NonNull public android.os.PersistableBundle getStallDetails(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.DataStallReport> CREATOR; + field public static final int DETECTION_METHOD_DNS_EVENTS = 1; // 0x1 + field public static final int DETECTION_METHOD_TCP_METRICS = 2; // 0x2 + field public static final String KEY_DNS_CONSECUTIVE_TIMEOUTS = "dnsConsecutiveTimeouts"; + field public static final String KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS = "tcpMetricsCollectionPeriodMillis"; + field public static final String KEY_TCP_PACKET_FAIL_RATE = "tcpPacketFailRate"; + } + + public class ConnectivityManager { + method public void addDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener); + method public boolean bindProcessToNetwork(@Nullable android.net.Network); + method @NonNull public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull android.net.IpSecManager.UdpEncapsulationSocket, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback); + method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network getActiveNetwork(); + method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getActiveNetworkInfo(); + method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo[] getAllNetworkInfo(); + method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network[] getAllNetworks(); + method @Deprecated public boolean getBackgroundDataSetting(); + method @Nullable public android.net.Network getBoundNetworkForProcess(); + method public int getConnectionOwnerUid(int, @NonNull java.net.InetSocketAddress, @NonNull java.net.InetSocketAddress); + method @Nullable public android.net.ProxyInfo getDefaultProxy(); + method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.LinkProperties getLinkProperties(@Nullable android.net.Network); + method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public int getMultipathPreference(@Nullable android.net.Network); + method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkCapabilities getNetworkCapabilities(@Nullable android.net.Network); + method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getNetworkInfo(int); + method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getNetworkInfo(@Nullable android.net.Network); + method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public int getNetworkPreference(); + method @Nullable public byte[] getNetworkWatchlistConfigHash(); + method @Deprecated @Nullable public static android.net.Network getProcessDefaultNetwork(); + method public int getRestrictBackgroundStatus(); + method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public boolean isActiveNetworkMetered(); + method public boolean isDefaultNetworkActive(); + method @Deprecated public static boolean isNetworkTypeValid(int); + method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback); + method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); + method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback); + method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); + method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.app.PendingIntent); + method public void releaseNetworkRequest(@NonNull android.app.PendingIntent); + method public void removeDefaultNetworkActiveListener(@NonNull android.net.ConnectivityManager.OnNetworkActiveListener); + method @Deprecated public void reportBadNetwork(@Nullable android.net.Network); + method public void reportNetworkConnectivity(@Nullable android.net.Network, boolean); + method public boolean requestBandwidthUpdate(@NonNull android.net.Network); + method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback); + method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); + method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, int); + method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler, int); + method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.app.PendingIntent); + method @Deprecated public void setNetworkPreference(int); + method @Deprecated public static boolean setProcessDefaultNetwork(@Nullable android.net.Network); + method public void unregisterNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback); + method public void unregisterNetworkCallback(@NonNull android.app.PendingIntent); + field @Deprecated public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED"; + field public static final String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL"; + field public static final String ACTION_RESTRICT_BACKGROUND_CHANGED = "android.net.conn.RESTRICT_BACKGROUND_CHANGED"; + field @Deprecated public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; + field @Deprecated public static final int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1 + field public static final String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL"; + field public static final String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL"; + field @Deprecated public static final String EXTRA_EXTRA_INFO = "extraInfo"; + field @Deprecated public static final String EXTRA_IS_FAILOVER = "isFailover"; + field public static final String EXTRA_NETWORK = "android.net.extra.NETWORK"; + field @Deprecated public static final String EXTRA_NETWORK_INFO = "networkInfo"; + field public static final String EXTRA_NETWORK_REQUEST = "android.net.extra.NETWORK_REQUEST"; + field @Deprecated public static final String EXTRA_NETWORK_TYPE = "networkType"; + field public static final String EXTRA_NO_CONNECTIVITY = "noConnectivity"; + field @Deprecated public static final String EXTRA_OTHER_NETWORK_INFO = "otherNetwork"; + field public static final String EXTRA_REASON = "reason"; + field public static final int MULTIPATH_PREFERENCE_HANDOVER = 1; // 0x1 + field public static final int MULTIPATH_PREFERENCE_PERFORMANCE = 4; // 0x4 + field public static final int MULTIPATH_PREFERENCE_RELIABILITY = 2; // 0x2 + field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1 + field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3 + field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2 + field @Deprecated public static final int TYPE_BLUETOOTH = 7; // 0x7 + field @Deprecated public static final int TYPE_DUMMY = 8; // 0x8 + field @Deprecated public static final int TYPE_ETHERNET = 9; // 0x9 + field @Deprecated public static final int TYPE_MOBILE = 0; // 0x0 + field @Deprecated public static final int TYPE_MOBILE_DUN = 4; // 0x4 + field @Deprecated public static final int TYPE_MOBILE_HIPRI = 5; // 0x5 + field @Deprecated public static final int TYPE_MOBILE_MMS = 2; // 0x2 + field @Deprecated public static final int TYPE_MOBILE_SUPL = 3; // 0x3 + field @Deprecated public static final int TYPE_VPN = 17; // 0x11 + field @Deprecated public static final int TYPE_WIFI = 1; // 0x1 + field @Deprecated public static final int TYPE_WIMAX = 6; // 0x6 + } + + public static class ConnectivityManager.NetworkCallback { + ctor public ConnectivityManager.NetworkCallback(); + method public void onAvailable(@NonNull android.net.Network); + method public void onBlockedStatusChanged(@NonNull android.net.Network, boolean); + method public void onCapabilitiesChanged(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities); + method public void onLinkPropertiesChanged(@NonNull android.net.Network, @NonNull android.net.LinkProperties); + method public void onLosing(@NonNull android.net.Network, int); + method public void onLost(@NonNull android.net.Network); + method public void onUnavailable(); + } + + public static interface ConnectivityManager.OnNetworkActiveListener { + method public void onNetworkActive(); + } + + public class DhcpInfo implements android.os.Parcelable { + ctor public DhcpInfo(); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.DhcpInfo> CREATOR; + field public int dns1; + field public int dns2; + field public int gateway; + field public int ipAddress; + field public int leaseDuration; + field public int netmask; + field public int serverAddress; + } + + public final class DnsResolver { + method @NonNull public static android.net.DnsResolver getInstance(); + method public void query(@Nullable android.net.Network, @NonNull String, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super java.util.List<java.net.InetAddress>>); + method public void query(@Nullable android.net.Network, @NonNull String, int, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super java.util.List<java.net.InetAddress>>); + method public void rawQuery(@Nullable android.net.Network, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super byte[]>); + method public void rawQuery(@Nullable android.net.Network, @NonNull String, int, int, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super byte[]>); + field public static final int CLASS_IN = 1; // 0x1 + field public static final int ERROR_PARSE = 0; // 0x0 + field public static final int ERROR_SYSTEM = 1; // 0x1 + field public static final int FLAG_EMPTY = 0; // 0x0 + field public static final int FLAG_NO_CACHE_LOOKUP = 4; // 0x4 + field public static final int FLAG_NO_CACHE_STORE = 2; // 0x2 + field public static final int FLAG_NO_RETRY = 1; // 0x1 + field public static final int TYPE_A = 1; // 0x1 + field public static final int TYPE_AAAA = 28; // 0x1c + } + + public static interface DnsResolver.Callback<T> { + method public void onAnswer(@NonNull T, int); + method public void onError(@NonNull android.net.DnsResolver.DnsException); + } + + public static class DnsResolver.DnsException extends java.lang.Exception { + field public final int code; + } + + public class InetAddresses { + method public static boolean isNumericAddress(@NonNull String); + method @NonNull public static java.net.InetAddress parseNumericAddress(@NonNull String); + } + + public final class IpPrefix implements android.os.Parcelable { + method public boolean contains(@NonNull java.net.InetAddress); + method public int describeContents(); + method @NonNull public java.net.InetAddress getAddress(); + method @IntRange(from=0, to=128) public int getPrefixLength(); + method @NonNull public byte[] getRawAddress(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.IpPrefix> CREATOR; + } + + public class LinkAddress implements android.os.Parcelable { + method public int describeContents(); + method public java.net.InetAddress getAddress(); + method public int getFlags(); + method @IntRange(from=0, to=128) public int getPrefixLength(); + method public int getScope(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.LinkAddress> CREATOR; + } + + public final class LinkProperties implements android.os.Parcelable { + ctor public LinkProperties(); + method public boolean addRoute(@NonNull android.net.RouteInfo); + method public void clear(); + method public int describeContents(); + method @Nullable public java.net.Inet4Address getDhcpServerAddress(); + method @NonNull public java.util.List<java.net.InetAddress> getDnsServers(); + method @Nullable public String getDomains(); + method @Nullable public android.net.ProxyInfo getHttpProxy(); + method @Nullable public String getInterfaceName(); + method @NonNull public java.util.List<android.net.LinkAddress> getLinkAddresses(); + method public int getMtu(); + method @Nullable public android.net.IpPrefix getNat64Prefix(); + method @Nullable public String getPrivateDnsServerName(); + method @NonNull public java.util.List<android.net.RouteInfo> getRoutes(); + method public boolean isPrivateDnsActive(); + method public boolean isWakeOnLanSupported(); + method public void setDhcpServerAddress(@Nullable java.net.Inet4Address); + method public void setDnsServers(@NonNull java.util.Collection<java.net.InetAddress>); + method public void setDomains(@Nullable String); + method public void setHttpProxy(@Nullable android.net.ProxyInfo); + method public void setInterfaceName(@Nullable String); + method public void setLinkAddresses(@NonNull java.util.Collection<android.net.LinkAddress>); + method public void setMtu(int); + method public void setNat64Prefix(@Nullable android.net.IpPrefix); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.LinkProperties> CREATOR; + } + + public final class MacAddress implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public static android.net.MacAddress fromBytes(@NonNull byte[]); + method @NonNull public static android.net.MacAddress fromString(@NonNull String); + method public int getAddressType(); + method @Nullable public java.net.Inet6Address getLinkLocalIpv6FromEui48Mac(); + method public boolean isLocallyAssigned(); + method public boolean matches(@NonNull android.net.MacAddress, @NonNull android.net.MacAddress); + method @NonNull public byte[] toByteArray(); + method @NonNull public String toOuiString(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.net.MacAddress BROADCAST_ADDRESS; + field @NonNull public static final android.os.Parcelable.Creator<android.net.MacAddress> CREATOR; + field public static final int TYPE_BROADCAST = 3; // 0x3 + field public static final int TYPE_MULTICAST = 2; // 0x2 + field public static final int TYPE_UNICAST = 1; // 0x1 + } + + public class Network implements android.os.Parcelable { + method public void bindSocket(java.net.DatagramSocket) throws java.io.IOException; + method public void bindSocket(java.net.Socket) throws java.io.IOException; + method public void bindSocket(java.io.FileDescriptor) throws java.io.IOException; + method public int describeContents(); + method public static android.net.Network fromNetworkHandle(long); + method public java.net.InetAddress[] getAllByName(String) throws java.net.UnknownHostException; + method public java.net.InetAddress getByName(String) throws java.net.UnknownHostException; + method public long getNetworkHandle(); + method public javax.net.SocketFactory getSocketFactory(); + method public java.net.URLConnection openConnection(java.net.URL) throws java.io.IOException; + method public java.net.URLConnection openConnection(java.net.URL, java.net.Proxy) throws java.io.IOException; + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.Network> CREATOR; + } + + public final class NetworkCapabilities implements android.os.Parcelable { + ctor public NetworkCapabilities(); + ctor public NetworkCapabilities(android.net.NetworkCapabilities); + method public int describeContents(); + method public int getLinkDownstreamBandwidthKbps(); + method public int getLinkUpstreamBandwidthKbps(); + method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier(); + method public int getOwnerUid(); + method public int getSignalStrength(); + method @Nullable public android.net.TransportInfo getTransportInfo(); + method public boolean hasCapability(int); + method public boolean hasTransport(int); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkCapabilities> CREATOR; + field public static final int NET_CAPABILITY_CAPTIVE_PORTAL = 17; // 0x11 + 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 + field public static final int NET_CAPABILITY_IMS = 4; // 0x4 + field public static final int NET_CAPABILITY_INTERNET = 12; // 0xc + field public static final int NET_CAPABILITY_MCX = 23; // 0x17 + field public static final int NET_CAPABILITY_MMS = 0; // 0x0 + field public static final int NET_CAPABILITY_NOT_CONGESTED = 20; // 0x14 + field public static final int NET_CAPABILITY_NOT_METERED = 11; // 0xb + field public static final int NET_CAPABILITY_NOT_RESTRICTED = 13; // 0xd + field public static final int NET_CAPABILITY_NOT_ROAMING = 18; // 0x12 + field public static final int NET_CAPABILITY_NOT_SUSPENDED = 21; // 0x15 + field public static final int NET_CAPABILITY_NOT_VPN = 15; // 0xf + field public static final int NET_CAPABILITY_RCS = 8; // 0x8 + field public static final int NET_CAPABILITY_SUPL = 1; // 0x1 + field public static final int NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25; // 0x19 + field public static final int NET_CAPABILITY_TRUSTED = 14; // 0xe + field public static final int NET_CAPABILITY_VALIDATED = 16; // 0x10 + field public static final int NET_CAPABILITY_WIFI_P2P = 6; // 0x6 + field public static final int NET_CAPABILITY_XCAP = 9; // 0x9 + field public static final int SIGNAL_STRENGTH_UNSPECIFIED = -2147483648; // 0x80000000 + field public static final int TRANSPORT_BLUETOOTH = 2; // 0x2 + field public static final int TRANSPORT_CELLULAR = 0; // 0x0 + field public static final int TRANSPORT_ETHERNET = 3; // 0x3 + field public static final int TRANSPORT_LOWPAN = 6; // 0x6 + field public static final int TRANSPORT_VPN = 4; // 0x4 + field public static final int TRANSPORT_WIFI = 1; // 0x1 + field public static final int TRANSPORT_WIFI_AWARE = 5; // 0x5 + } + + @Deprecated public class NetworkInfo implements android.os.Parcelable { + ctor @Deprecated public NetworkInfo(int, int, @Nullable String, @Nullable String); + method @Deprecated public int describeContents(); + method @Deprecated @NonNull public android.net.NetworkInfo.DetailedState getDetailedState(); + method @Deprecated public String getExtraInfo(); + method @Deprecated public String getReason(); + method @Deprecated public android.net.NetworkInfo.State getState(); + method @Deprecated public int getSubtype(); + method @Deprecated public String getSubtypeName(); + method @Deprecated public int getType(); + method @Deprecated public String getTypeName(); + method @Deprecated public boolean isAvailable(); + method @Deprecated public boolean isConnected(); + method @Deprecated public boolean isConnectedOrConnecting(); + method @Deprecated public boolean isFailover(); + method @Deprecated public boolean isRoaming(); + method @Deprecated public void setDetailedState(@NonNull android.net.NetworkInfo.DetailedState, @Nullable String, @Nullable String); + method @Deprecated public void writeToParcel(android.os.Parcel, int); + field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkInfo> CREATOR; + } + + @Deprecated public enum NetworkInfo.DetailedState { + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState AUTHENTICATING; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState BLOCKED; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CAPTIVE_PORTAL_CHECK; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CONNECTED; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CONNECTING; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState DISCONNECTED; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState DISCONNECTING; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState FAILED; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState IDLE; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState OBTAINING_IPADDR; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState SCANNING; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState SUSPENDED; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState VERIFYING_POOR_LINK; + } + + @Deprecated public enum NetworkInfo.State { + enum_constant @Deprecated public static final android.net.NetworkInfo.State CONNECTED; + enum_constant @Deprecated public static final android.net.NetworkInfo.State CONNECTING; + enum_constant @Deprecated public static final android.net.NetworkInfo.State DISCONNECTED; + enum_constant @Deprecated public static final android.net.NetworkInfo.State DISCONNECTING; + enum_constant @Deprecated public static final android.net.NetworkInfo.State SUSPENDED; + enum_constant @Deprecated public static final android.net.NetworkInfo.State UNKNOWN; + } + + public class NetworkRequest implements android.os.Parcelable { + method public boolean canBeSatisfiedBy(@Nullable android.net.NetworkCapabilities); + method public int describeContents(); + method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier(); + method public boolean hasCapability(int); + method public boolean hasTransport(int); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkRequest> CREATOR; + } + + public static class NetworkRequest.Builder { + ctor public NetworkRequest.Builder(); + method public android.net.NetworkRequest.Builder addCapability(int); + method public android.net.NetworkRequest.Builder addTransportType(int); + method public android.net.NetworkRequest build(); + method @NonNull public android.net.NetworkRequest.Builder clearCapabilities(); + method public android.net.NetworkRequest.Builder removeCapability(int); + method public android.net.NetworkRequest.Builder removeTransportType(int); + method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String); + method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier); + } + + public final class Proxy { + ctor public Proxy(); + method @Deprecated public static String getDefaultHost(); + method @Deprecated public static int getDefaultPort(); + method @Deprecated public static String getHost(android.content.Context); + method @Deprecated public static int getPort(android.content.Context); + field @Deprecated public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO"; + field public static final String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE"; + } + + public class ProxyInfo implements android.os.Parcelable { + ctor public ProxyInfo(@Nullable android.net.ProxyInfo); + method public static android.net.ProxyInfo buildDirectProxy(String, int); + method public static android.net.ProxyInfo buildDirectProxy(String, int, java.util.List<java.lang.String>); + method public static android.net.ProxyInfo buildPacProxy(android.net.Uri); + method @NonNull public static android.net.ProxyInfo buildPacProxy(@NonNull android.net.Uri, int); + method public int describeContents(); + method public String[] getExclusionList(); + method public String getHost(); + method public android.net.Uri getPacFileUrl(); + method public int getPort(); + method public boolean isValid(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.ProxyInfo> CREATOR; + } + + public final class RouteInfo implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.net.IpPrefix getDestination(); + method @Nullable public java.net.InetAddress getGateway(); + method @Nullable public String getInterface(); + method public boolean hasGateway(); + method public boolean isDefaultRoute(); + method public boolean matches(java.net.InetAddress); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.RouteInfo> CREATOR; + } + + public abstract class SocketKeepalive implements java.lang.AutoCloseable { + method public final void close(); + method public final void start(@IntRange(from=0xa, to=0xe10) int); + method public final void stop(); + field public static final int ERROR_HARDWARE_ERROR = -31; // 0xffffffe1 + field public static final int ERROR_INSUFFICIENT_RESOURCES = -32; // 0xffffffe0 + field public static final int ERROR_INVALID_INTERVAL = -24; // 0xffffffe8 + field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb + field public static final int ERROR_INVALID_LENGTH = -23; // 0xffffffe9 + field public static final int ERROR_INVALID_NETWORK = -20; // 0xffffffec + field public static final int ERROR_INVALID_PORT = -22; // 0xffffffea + field public static final int ERROR_INVALID_SOCKET = -25; // 0xffffffe7 + field public static final int ERROR_SOCKET_NOT_IDLE = -26; // 0xffffffe6 + field public static final int ERROR_UNSUPPORTED = -30; // 0xffffffe2 + } + + public static class SocketKeepalive.Callback { + ctor public SocketKeepalive.Callback(); + method public void onDataReceived(); + method public void onError(int); + method public void onStarted(); + method public void onStopped(); + } + + public interface TransportInfo { + } + +} + diff --git a/packages/Connectivity/framework/api/lint-baseline.txt b/packages/Connectivity/framework/api/lint-baseline.txt new file mode 100644 index 000000000000..2f4004ab723d --- /dev/null +++ b/packages/Connectivity/framework/api/lint-baseline.txt @@ -0,0 +1,4 @@ +// Baseline format: 1.0 +VisiblySynchronized: android.net.NetworkInfo#toString(): + Internal locks must not be exposed (synchronizing on this or class is still + externally observable): method android.net.NetworkInfo.toString() diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt new file mode 100644 index 000000000000..3af855ec1e11 --- /dev/null +++ b/packages/Connectivity/framework/api/module-lib-current.txt @@ -0,0 +1,66 @@ +// Signature format: 2.0 +package android.net { + + public final class ConnectivityFrameworkInitializer { + method public static void registerServiceWrappers(); + } + + public class ConnectivityManager { + method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @Nullable android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle); + } + + public final class NetworkAgentConfig implements android.os.Parcelable { + method @Nullable public String getSubscriberId(); + } + + public static final class NetworkAgentConfig.Builder { + method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String); + } + + public final class NetworkCapabilities implements android.os.Parcelable { + field public static final int TRANSPORT_TEST = 7; // 0x7 + } + + public final class Proxy { + method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo); + } + + public final class TcpRepairWindow { + ctor public TcpRepairWindow(int, int, int, int, int, int); + field public final int maxWindow; + field public final int rcvWnd; + field public final int rcvWndScale; + field public final int rcvWup; + field public final int sndWl1; + field public final int sndWnd; + } + + public final class TestNetworkInterface implements android.os.Parcelable { + ctor public TestNetworkInterface(@NonNull android.os.ParcelFileDescriptor, @NonNull String); + method public int describeContents(); + method @NonNull public android.os.ParcelFileDescriptor getFileDescriptor(); + method @NonNull public String getInterfaceName(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.TestNetworkInterface> CREATOR; + } + + public class TestNetworkManager { + method @NonNull public android.net.TestNetworkInterface createTapInterface(); + method @NonNull public android.net.TestNetworkInterface createTunInterface(@NonNull java.util.Collection<android.net.LinkAddress>); + method public void setupTestNetwork(@NonNull String, @NonNull android.os.IBinder); + method public void teardownTestNetwork(@NonNull android.net.Network); + field public static final String TEST_TAP_PREFIX = "testtap"; + } + + public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo { + ctor public VpnTransportInfo(int); + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.VpnTransportInfo> CREATOR; + field public final int type; + } + +} + diff --git a/packages/Connectivity/framework/api/module-lib-removed.txt b/packages/Connectivity/framework/api/module-lib-removed.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/packages/Connectivity/framework/api/module-lib-removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/packages/Connectivity/framework/api/removed.txt b/packages/Connectivity/framework/api/removed.txt new file mode 100644 index 000000000000..303a1e6173ba --- /dev/null +++ b/packages/Connectivity/framework/api/removed.txt @@ -0,0 +1,11 @@ +// Signature format: 2.0 +package android.net { + + public class ConnectivityManager { + method @Deprecated public boolean requestRouteToHost(int, int); + method @Deprecated public int startUsingNetworkFeature(int, String); + method @Deprecated public int stopUsingNetworkFeature(int, String); + } + +} + diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt new file mode 100644 index 000000000000..41ebc5774f3d --- /dev/null +++ b/packages/Connectivity/framework/api/system-current.txt @@ -0,0 +1,407 @@ +// Signature format: 2.0 +package android.net { + + public class CaptivePortal implements android.os.Parcelable { + method public void logEvent(int, @NonNull String); + method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void reevaluateNetwork(); + method public void useNetwork(); + field public static final int APP_REQUEST_REEVALUATION_REQUIRED = 100; // 0x64 + field public static final int APP_RETURN_DISMISSED = 0; // 0x0 + field public static final int APP_RETURN_UNWANTED = 1; // 0x1 + field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2 + } + + public final class CaptivePortalData implements android.os.Parcelable { + method public int describeContents(); + method public long getByteLimit(); + method public long getExpiryTimeMillis(); + method public long getRefreshTimeMillis(); + method @Nullable public android.net.Uri getUserPortalUrl(); + method public int getUserPortalUrlSource(); + method @Nullable public String getVenueFriendlyName(); + method @Nullable public android.net.Uri getVenueInfoUrl(); + method public int getVenueInfoUrlSource(); + method public boolean isCaptive(); + method public boolean isSessionExtendable(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int CAPTIVE_PORTAL_DATA_SOURCE_OTHER = 0; // 0x0 + field public static final int CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT = 1; // 0x1 + field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortalData> CREATOR; + } + + public static class CaptivePortalData.Builder { + ctor public CaptivePortalData.Builder(); + ctor public CaptivePortalData.Builder(@Nullable android.net.CaptivePortalData); + method @NonNull public android.net.CaptivePortalData build(); + method @NonNull public android.net.CaptivePortalData.Builder setBytesRemaining(long); + method @NonNull public android.net.CaptivePortalData.Builder setCaptive(boolean); + method @NonNull public android.net.CaptivePortalData.Builder setExpiryTime(long); + method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long); + method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean); + method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri); + method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri, int); + method @NonNull public android.net.CaptivePortalData.Builder setVenueFriendlyName(@Nullable String); + method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri); + method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri, int); + } + + public class ConnectivityManager { + method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull android.os.ParcelFileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback); + method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull java.net.Socket, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback); + method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCaptivePortalServerUrl(); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported(); + method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public int registerNetworkProvider(@NonNull android.net.NetworkProvider); + method public void registerQosCallback(@NonNull android.net.QosSocketInfo, @NonNull android.net.QosCallback, @NonNull java.util.concurrent.Executor); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback); + method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void requestNetwork(@NonNull android.net.NetworkRequest, int, int, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean); + method @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE) public void setOemNetworkPreference(@NonNull android.net.OemNetworkPreferences, @Nullable java.util.concurrent.Executor, @Nullable android.net.ConnectivityManager.OnSetOemNetworkPreferenceListener); + method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi(); + method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int); + method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider); + method public void unregisterQosCallback(@NonNull android.net.QosCallback); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback); + field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC"; + field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT"; + field public static final int TETHERING_BLUETOOTH = 2; // 0x2 + field public static final int TETHERING_USB = 1; // 0x1 + field public static final int TETHERING_WIFI = 0; // 0x0 + field @Deprecated public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; // 0xd + field @Deprecated public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 + field @Deprecated public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb + field public static final int TYPE_NONE = -1; // 0xffffffff + field @Deprecated public static final int TYPE_PROXY = 16; // 0x10 + field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd + } + + public static interface ConnectivityManager.OnSetOemNetworkPreferenceListener { + method public void onComplete(); + } + + @Deprecated public abstract static class ConnectivityManager.OnStartTetheringCallback { + ctor @Deprecated public ConnectivityManager.OnStartTetheringCallback(); + method @Deprecated public void onTetheringFailed(); + method @Deprecated public void onTetheringStarted(); + } + + @Deprecated public static interface ConnectivityManager.OnTetheringEntitlementResultListener { + method @Deprecated public void onTetheringEntitlementResult(int); + } + + @Deprecated public abstract static class ConnectivityManager.OnTetheringEventCallback { + ctor @Deprecated public ConnectivityManager.OnTetheringEventCallback(); + method @Deprecated public void onUpstreamChanged(@Nullable android.net.Network); + } + + public final class InvalidPacketException extends java.lang.Exception { + ctor public InvalidPacketException(int); + method public int getError(); + field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb + field public static final int ERROR_INVALID_LENGTH = -23; // 0xffffffe9 + field public static final int ERROR_INVALID_PORT = -22; // 0xffffffea + } + + public final class IpConfiguration implements android.os.Parcelable { + ctor public IpConfiguration(); + ctor public IpConfiguration(@NonNull android.net.IpConfiguration); + method public int describeContents(); + method @Nullable public android.net.ProxyInfo getHttpProxy(); + method @NonNull public android.net.IpConfiguration.IpAssignment getIpAssignment(); + method @NonNull public android.net.IpConfiguration.ProxySettings getProxySettings(); + method @Nullable public android.net.StaticIpConfiguration getStaticIpConfiguration(); + method public void setHttpProxy(@Nullable android.net.ProxyInfo); + method public void setIpAssignment(@NonNull android.net.IpConfiguration.IpAssignment); + method public void setProxySettings(@NonNull android.net.IpConfiguration.ProxySettings); + method public void setStaticIpConfiguration(@Nullable android.net.StaticIpConfiguration); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.IpConfiguration> CREATOR; + } + + public enum IpConfiguration.IpAssignment { + enum_constant public static final android.net.IpConfiguration.IpAssignment DHCP; + enum_constant public static final android.net.IpConfiguration.IpAssignment STATIC; + enum_constant public static final android.net.IpConfiguration.IpAssignment UNASSIGNED; + } + + public enum IpConfiguration.ProxySettings { + enum_constant public static final android.net.IpConfiguration.ProxySettings NONE; + enum_constant public static final android.net.IpConfiguration.ProxySettings PAC; + enum_constant public static final android.net.IpConfiguration.ProxySettings STATIC; + enum_constant public static final android.net.IpConfiguration.ProxySettings UNASSIGNED; + } + + public final class IpPrefix implements android.os.Parcelable { + ctor public IpPrefix(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int); + ctor public IpPrefix(@NonNull String); + } + + public class KeepalivePacketData { + ctor protected KeepalivePacketData(@NonNull java.net.InetAddress, @IntRange(from=0, to=65535) int, @NonNull java.net.InetAddress, @IntRange(from=0, to=65535) int, @NonNull byte[]) throws android.net.InvalidPacketException; + method @NonNull public java.net.InetAddress getDstAddress(); + method public int getDstPort(); + method @NonNull public byte[] getPacket(); + method @NonNull public java.net.InetAddress getSrcAddress(); + method public int getSrcPort(); + } + + public class LinkAddress implements android.os.Parcelable { + ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int); + ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int, long, long); + ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int); + ctor public LinkAddress(@NonNull String); + ctor public LinkAddress(@NonNull String, int, int); + method public long getDeprecationTime(); + method public long getExpirationTime(); + method public boolean isGlobalPreferred(); + method public boolean isIpv4(); + method public boolean isIpv6(); + method public boolean isSameAddressAs(@Nullable android.net.LinkAddress); + field public static final long LIFETIME_PERMANENT = 9223372036854775807L; // 0x7fffffffffffffffL + field public static final long LIFETIME_UNKNOWN = -1L; // 0xffffffffffffffffL + } + + public final class LinkProperties implements android.os.Parcelable { + ctor public LinkProperties(@Nullable android.net.LinkProperties); + ctor public LinkProperties(@Nullable android.net.LinkProperties, boolean); + method public boolean addDnsServer(@NonNull java.net.InetAddress); + method public boolean addLinkAddress(@NonNull android.net.LinkAddress); + method public boolean addPcscfServer(@NonNull java.net.InetAddress); + method @NonNull public java.util.List<java.net.InetAddress> getAddresses(); + method @NonNull public java.util.List<java.lang.String> getAllInterfaceNames(); + method @NonNull public java.util.List<android.net.LinkAddress> getAllLinkAddresses(); + method @NonNull public java.util.List<android.net.RouteInfo> getAllRoutes(); + method @Nullable public android.net.Uri getCaptivePortalApiUrl(); + method @Nullable public android.net.CaptivePortalData getCaptivePortalData(); + method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers(); + method @Nullable public String getTcpBufferSizes(); + method @NonNull public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers(); + method public boolean hasGlobalIpv6Address(); + method public boolean hasIpv4Address(); + method public boolean hasIpv4DefaultRoute(); + method public boolean hasIpv4DnsServer(); + method public boolean hasIpv6DefaultRoute(); + method public boolean hasIpv6DnsServer(); + method public boolean isIpv4Provisioned(); + method public boolean isIpv6Provisioned(); + method public boolean isProvisioned(); + method public boolean isReachable(@NonNull java.net.InetAddress); + method public boolean removeDnsServer(@NonNull java.net.InetAddress); + method public boolean removeLinkAddress(@NonNull android.net.LinkAddress); + method public boolean removeRoute(@NonNull android.net.RouteInfo); + method public void setCaptivePortalApiUrl(@Nullable android.net.Uri); + method public void setCaptivePortalData(@Nullable android.net.CaptivePortalData); + method public void setPcscfServers(@NonNull java.util.Collection<java.net.InetAddress>); + method public void setPrivateDnsServerName(@Nullable String); + method public void setTcpBufferSizes(@Nullable String); + method public void setUsePrivateDns(boolean); + method public void setValidatedPrivateDnsServers(@NonNull java.util.Collection<java.net.InetAddress>); + } + + public final class NattKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable { + ctor public NattKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[]) throws android.net.InvalidPacketException; + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.NattKeepalivePacketData> CREATOR; + } + + public class Network implements android.os.Parcelable { + ctor public Network(@NonNull android.net.Network); + method public int getNetId(); + method @NonNull public android.net.Network getPrivateDnsBypassingCopy(); + } + + public abstract class NetworkAgent { + ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, int, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider); + method @Nullable public android.net.Network getNetwork(); + method public void markConnected(); + method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData); + method public void onAutomaticReconnectDisabled(); + method public void onNetworkUnwanted(); + method public void onQosCallbackRegistered(int, @NonNull android.net.QosFilter); + method public void onQosCallbackUnregistered(int); + method public void onRemoveKeepalivePacketFilter(int); + method public void onSaveAcceptUnvalidated(boolean); + method public void onSignalStrengthThresholdsUpdated(@NonNull int[]); + method public void onStartSocketKeepalive(int, @NonNull java.time.Duration, @NonNull android.net.KeepalivePacketData); + method public void onStopSocketKeepalive(int); + method public void onValidationStatus(int, @Nullable android.net.Uri); + method @NonNull public android.net.Network register(); + method public final void sendLinkProperties(@NonNull android.net.LinkProperties); + method public final void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities); + method public final void sendNetworkScore(@IntRange(from=0, to=99) int); + method public final void sendQosCallbackError(int, int); + method public final void sendQosSessionAvailable(int, int, @NonNull android.telephony.data.EpsBearerQosSessionAttributes); + method public final void sendQosSessionLost(int, int); + method public final void sendSocketKeepaliveEvent(int, int); + method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>); + method public void unregister(); + field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2 + field public static final int VALIDATION_STATUS_VALID = 1; // 0x1 + } + + public final class NetworkAgentConfig implements android.os.Parcelable { + method public int describeContents(); + method public int getLegacyType(); + method @NonNull public String getLegacyTypeName(); + method public boolean isExplicitlySelected(); + method public boolean isPartialConnectivityAcceptable(); + method public boolean isUnvalidatedConnectivityAcceptable(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkAgentConfig> CREATOR; + } + + public static final class NetworkAgentConfig.Builder { + ctor public NetworkAgentConfig.Builder(); + method @NonNull public android.net.NetworkAgentConfig build(); + method @NonNull public android.net.NetworkAgentConfig.Builder setExplicitlySelected(boolean); + method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyType(int); + method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyTypeName(@NonNull String); + method @NonNull public android.net.NetworkAgentConfig.Builder setPartialConnectivityAcceptable(boolean); + method @NonNull public android.net.NetworkAgentConfig.Builder setUnvalidatedConnectivityAcceptable(boolean); + } + + public final class NetworkCapabilities implements android.os.Parcelable { + ctor public NetworkCapabilities(@Nullable android.net.NetworkCapabilities, boolean); + method @NonNull public int[] getAdministratorUids(); + method @Nullable public String getSsid(); + method @NonNull public int[] getTransportTypes(); + method public boolean isPrivateDnsBroken(); + method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities); + field public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; // 0x1c + field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16 + field public static final int NET_CAPABILITY_OEM_PRIVATE = 26; // 0x1a + field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18 + field public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27; // 0x1b + } + + public static final class NetworkCapabilities.Builder { + ctor public NetworkCapabilities.Builder(); + ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities); + method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int); + method @NonNull public android.net.NetworkCapabilities.Builder addTransportType(int); + method @NonNull public android.net.NetworkCapabilities build(); + method @NonNull public android.net.NetworkCapabilities.Builder clearAll(); + method @NonNull public android.net.NetworkCapabilities.Builder removeCapability(int); + method @NonNull public android.net.NetworkCapabilities.Builder removeTransportType(int); + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setAdministratorUids(@NonNull int[]); + method @NonNull public android.net.NetworkCapabilities.Builder setLinkDownstreamBandwidthKbps(int); + method @NonNull public android.net.NetworkCapabilities.Builder setLinkUpstreamBandwidthKbps(int); + method @NonNull public android.net.NetworkCapabilities.Builder setNetworkSpecifier(@Nullable android.net.NetworkSpecifier); + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setOwnerUid(int); + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorPackageName(@Nullable String); + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorUid(int); + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkCapabilities.Builder setSignalStrength(int); + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String); + method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo); + } + + public class NetworkProvider { + ctor public NetworkProvider(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String); + method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void declareNetworkRequestUnfulfillable(@NonNull android.net.NetworkRequest); + method public int getProviderId(); + method public void onNetworkRequestWithdrawn(@NonNull android.net.NetworkRequest); + method public void onNetworkRequested(@NonNull android.net.NetworkRequest, @IntRange(from=0, to=99) int, int); + field public static final int ID_NONE = -1; // 0xffffffff + } + + public class NetworkRequest implements android.os.Parcelable { + method @Nullable public String getRequestorPackageName(); + method public int getRequestorUid(); + } + + public static class NetworkRequest.Builder { + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int); + } + + public final class RouteInfo implements android.os.Parcelable { + ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int); + ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int, int); + method public int getMtu(); + method public int getType(); + field public static final int RTN_THROW = 9; // 0x9 + field public static final int RTN_UNICAST = 1; // 0x1 + field public static final int RTN_UNREACHABLE = 7; // 0x7 + } + + public abstract class SocketKeepalive implements java.lang.AutoCloseable { + field public static final int SUCCESS = 0; // 0x0 + } + + public final class StaticIpConfiguration implements android.os.Parcelable { + ctor public StaticIpConfiguration(); + ctor public StaticIpConfiguration(@Nullable android.net.StaticIpConfiguration); + method public void addDnsServer(@NonNull java.net.InetAddress); + method public void clear(); + method public int describeContents(); + method @NonNull public java.util.List<java.net.InetAddress> getDnsServers(); + method @Nullable public String getDomains(); + method @Nullable public java.net.InetAddress getGateway(); + method @Nullable public android.net.LinkAddress getIpAddress(); + method @NonNull public java.util.List<android.net.RouteInfo> getRoutes(@Nullable String); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.StaticIpConfiguration> CREATOR; + } + + public static final class StaticIpConfiguration.Builder { + ctor public StaticIpConfiguration.Builder(); + method @NonNull public android.net.StaticIpConfiguration build(); + method @NonNull public android.net.StaticIpConfiguration.Builder setDnsServers(@NonNull Iterable<java.net.InetAddress>); + method @NonNull public android.net.StaticIpConfiguration.Builder setDomains(@Nullable String); + method @NonNull public android.net.StaticIpConfiguration.Builder setGateway(@Nullable java.net.InetAddress); + method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@Nullable android.net.LinkAddress); + } + + public final class TcpKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable { + ctor public TcpKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[], int, int, int, int, int, int) throws android.net.InvalidPacketException; + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.TcpKeepalivePacketData> CREATOR; + field public final int ipTos; + field public final int ipTtl; + field public final int tcpAck; + field public final int tcpSeq; + field public final int tcpWindow; + field public final int tcpWindowScale; + } + + public interface TransportInfo { + method public default boolean hasLocationSensitiveFields(); + method @NonNull public default android.net.TransportInfo makeCopy(boolean); + } + +} + +package android.net.apf { + + public final class ApfCapabilities implements android.os.Parcelable { + ctor public ApfCapabilities(int, int, int); + method public int describeContents(); + method public static boolean getApfDrop8023Frames(); + method @NonNull public static int[] getApfEtherTypeBlackList(); + method public boolean hasDataAccess(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.apf.ApfCapabilities> CREATOR; + field public final int apfPacketFormat; + field public final int apfVersionSupported; + field public final int maximumApfProgramSize; + } + +} + +package android.net.util { + + public final class SocketUtils { + method public static void bindSocketToInterface(@NonNull java.io.FileDescriptor, @NonNull String) throws android.system.ErrnoException; + method public static void closeSocket(@Nullable java.io.FileDescriptor) throws java.io.IOException; + method @NonNull public static java.net.SocketAddress makeNetlinkSocketAddress(int, int); + method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int); + method @Deprecated @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, @NonNull byte[]); + method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int, @NonNull byte[]); + } + +} + diff --git a/packages/Connectivity/framework/api/system-lint-baseline.txt b/packages/Connectivity/framework/api/system-lint-baseline.txt new file mode 100644 index 000000000000..9a97707763f1 --- /dev/null +++ b/packages/Connectivity/framework/api/system-lint-baseline.txt @@ -0,0 +1 @@ +// Baseline format: 1.0 diff --git a/packages/Connectivity/framework/api/system-removed.txt b/packages/Connectivity/framework/api/system-removed.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/packages/Connectivity/framework/api/system-removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 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/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java index 66165b6d1ff2..ad6a5312f156 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java @@ -147,5 +147,10 @@ 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 664fd4ac2fcb..303e5bb7050d 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -283,6 +283,7 @@ 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, diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 259484073162..71e09106368b 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -84,8 +84,6 @@ <uses-permission android:name="android.permission.READ_INPUT_STATE" /> <uses-permission android:name="android.permission.SET_ORIENTATION" /> <uses-permission android:name="android.permission.INSTALL_PACKAGES" /> - <!-- TODO(b/152310230): remove once APIs are confirmed to be sufficient --> - <uses-permission android:name="com.android.permission.USE_INSTALLER_V2" /> <uses-permission android:name="com.android.permission.USE_SYSTEM_DATA_LOADERS" /> <uses-permission android:name="android.permission.MOVE_PACKAGE" /> <uses-permission android:name="android.permission.KEEP_UNINSTALLED_PACKAGES" /> @@ -125,6 +123,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"/> @@ -398,6 +398,7 @@ <!-- Permission required for CTS to test sensor privacy behavior --> <uses-permission android:name="android.permission.MANAGE_SENSOR_PRIVACY" /> + <uses-permission android:name="android.permission.OBSERVE_SENSOR_PRIVACY" /> <!-- Permission needed for CTS test - CallLogTest --> <uses-permission android:name="com.android.voicemail.permission.READ_VOICEMAIL" /> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index fbe58c505662..044216e5423d 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -87,6 +87,7 @@ <uses-permission android:name="android.permission.MASTER_CLEAR" /> <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.MANAGE_SENSOR_PRIVACY" /> + <uses-permission android:name="android.permission.OBSERVE_SENSOR_PRIVACY" /> <!-- ActivityManager --> <uses-permission android:name="android.permission.REAL_GET_TASKS" /> 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/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml index 0822947e8b16..93dd1a12f147 100644 --- a/packages/SystemUI/res/layout/qs_footer_impl.xml +++ b/packages/SystemUI/res/layout/qs_footer_impl.xml @@ -81,6 +81,22 @@ android:layout_height="match_parent" android:layout_weight="@integer/qs_footer_actions_weight" android:gravity="center_vertical|end" > + + <com.android.systemui.statusbar.AlphaOptimizedImageView + android:id="@+id/pm_lite" + android:layout_width="@dimen/qs_footer_action_button_size" + android:layout_height="@dimen/qs_footer_action_button_size" + android:background="?android:attr/selectableItemBackgroundBorderless" + android:clickable="true" + android:clipToPadding="false" + android:contentDescription="@string/accessibility_quick_settings_edit" + android:focusable="true" + android:padding="@dimen/qs_footer_icon_padding" + android:src="@*android:drawable/ic_lock_power_off" + android:tint="?android:attr/colorForeground" + android:visibility="gone" + /> + <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch" android:layout_width="@dimen/qs_footer_action_button_size" diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml index 571cbbcc77db..1ce87c1c0236 100644 --- a/packages/SystemUI/res/layout/qs_tile_label.xml +++ b/packages/SystemUI/res/layout/qs_tile_label.xml @@ -20,7 +20,6 @@ android:layout_height="wrap_content" android:clipChildren="false" android:clipToPadding="false" - android:minHeight="48dp" android:paddingTop="12dp"> <LinearLayout android:id="@+id/label_group" diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index 4ca59f5082ad..bbb6107d149e 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -55,12 +55,14 @@ android:id="@+id/qs_frame" android:layout="@layout/qs_panel" android:layout_width="@dimen/qs_panel_width" - android:layout_height="match_parent" + android:layout_height="0dp" android:clipToPadding="false" android:clipChildren="false" systemui:viewType="com.android.systemui.plugins.qs.QS" systemui:layout_constraintStart_toStartOf="parent" systemui:layout_constraintEnd_toEndOf="parent" + systemui:layout_constraintTop_toTopOf="parent" + systemui:layout_constraintBottom_toBottomOf="parent" /> <androidx.constraintlayout.widget.Guideline diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 13c01102d032..0893c1488005 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -91,6 +91,9 @@ <!-- The number of columns in the QuickSettings --> <integer name="quick_settings_num_columns">3</integer> + <!-- The number of columns in the Quick Settings customizer --> + <integer name="quick_settings_edit_num_columns">@integer/quick_settings_num_columns</integer> + <!-- The number of rows in the QuickSettings --> <integer name="quick_settings_max_rows">3</integer> @@ -421,6 +424,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 +580,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/flags.xml b/packages/SystemUI/res/values/flags.xml index 3e16cd47336c..e5518928c98c 100644 --- a/packages/SystemUI/res/values/flags.xml +++ b/packages/SystemUI/res/values/flags.xml @@ -44,4 +44,6 @@ <bool name="flag_toast_style">false</bool> <bool name="flag_navigation_bar_overlay">false</bool> + + <bool name="flag_pm_lite">false</bool> </resources> 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/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java index 8cb1bc4878a5..5db3349b646a 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java @@ -69,7 +69,9 @@ public class NumPadButton extends AlphaOptimizedImageButton { @Override public boolean onTouchEvent(MotionEvent event) { - if (mAnimator != null) mAnimator.start(); + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + if (mAnimator != null) mAnimator.start(); + } return super.onTouchEvent(event); } diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java index a4a781dc6ff5..e6a9d4fdd547 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java @@ -160,10 +160,9 @@ public class NumPadKey extends ViewGroup { public boolean onTouchEvent(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { doHapticKeyClick(); + if (mAnimator != null) mAnimator.start(); } - if (mAnimator != null) mAnimator.start(); - return super.onTouchEvent(event); } diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index 9686c91f2c31..79f0688db869 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -16,8 +16,8 @@ package com.android.systemui.appops; -import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA; -import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE; +import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; +import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE; import static android.media.AudioManager.ACTION_MICROPHONE_MUTE_CHANGED; import android.Manifest; @@ -137,8 +137,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon mAudioManager = audioManager; mSensorPrivacyController = sensorPrivacyController; mMicMuted = audioManager.isMicrophoneMute() - || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE); - mCameraDisabled = mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA); + || mSensorPrivacyController.isSensorBlocked(MICROPHONE); + mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA); mLocationManager = context.getSystemService(LocationManager.class); mPackageManager = context.getPackageManager(); dumpManager.registerDumpable(TAG, this); @@ -159,8 +159,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon mSensorPrivacyController.addCallback(this); mMicMuted = mAudioManager.isMicrophoneMute() - || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE); - mCameraDisabled = mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA); + || mSensorPrivacyController.isSensorBlocked(MICROPHONE); + mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA); mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged( mAudioManager.getActiveRecordingConfigurations())); @@ -583,16 +583,16 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon @Override public void onReceive(Context context, Intent intent) { mMicMuted = mAudioManager.isMicrophoneMute() - || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE); + || mSensorPrivacyController.isSensorBlocked(MICROPHONE); updateSensorDisabledStatus(); } @Override public void onSensorBlockedChanged(int sensor, boolean blocked) { mBGHandler.post(() -> { - if (sensor == INDIVIDUAL_SENSOR_CAMERA) { + if (sensor == CAMERA) { mCameraDisabled = blocked; - } else if (sensor == INDIVIDUAL_SENSOR_MICROPHONE) { + } else if (sensor == MICROPHONE) { mMicMuted = mAudioManager.isMicrophoneMute() || blocked; } updateSensorDisabledStatus(); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java index d4678f39e404..127128dda112 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java @@ -103,6 +103,11 @@ public class KeyguardIndicationRotateTextViewController extends */ public void updateIndication(@IndicationType int type, KeyguardIndication newIndication, boolean showImmediately) { + if (type == INDICATION_TYPE_NOW_PLAYING + || type == INDICATION_TYPE_REVERSE_CHARGING) { + // temporarily don't show here, instead use AmbientContainer b/181049781 + return; + } final boolean hasPreviousIndication = mIndicationMessages.get(type) != null; final boolean hasNewIndication = newIndication != null; if (!hasNewIndication) { 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/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java index 2f9b17aece8e..2ea8657f88e5 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java @@ -19,8 +19,6 @@ package com.android.systemui.people; import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID; import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID; -import static com.android.systemui.people.PeopleSpaceUtils.getUserHandle; - import android.app.Activity; import android.app.INotificationManager; import android.app.people.IPeopleManager; @@ -155,7 +153,7 @@ public class PeopleSpaceActivity extends Activity { if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + tile.getId()); mLauncherApps.cacheShortcuts(tile.getPackageName(), Collections.singletonList(tile.getId()), - getUserHandle(tile), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS); + tile.getUserHandle(), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS); } catch (Exception e) { Log.w(TAG, "Exception caching shortcut:" + e); } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java index 9ae7847031aa..6f89332e66a9 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java @@ -21,7 +21,6 @@ import android.content.Context; import android.content.pm.LauncherApps; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; -import android.os.UserHandle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -82,7 +81,7 @@ public class PeopleSpaceTileView extends LinearLayout { public void setOnClickListener(LauncherApps launcherApps, PeopleSpaceTile tile) { mTileView.setOnClickListener(v -> launcherApps.startShortcut(tile.getPackageName(), tile.getId(), null, null, - UserHandle.getUserHandleForUid(tile.getUid()))); + tile.getUserHandle())); } /** Sets the click listener of the tile directly. */ diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java index 5dda23e4a47e..41080afb3604 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java @@ -55,7 +55,6 @@ import android.net.Uri; import android.os.Bundle; import android.os.Parcelable; import android.os.ServiceManager; -import android.os.UserHandle; import android.provider.ContactsContract; import android.provider.Settings; import android.service.notification.ConversationChannelWrapper; @@ -160,7 +159,6 @@ public class PeopleSpaceUtils { List<ConversationChannelWrapper> conversations = notificationManager.getConversations( false).getList(); - // Add priority conversations to tiles list. Stream<ShortcutInfo> priorityConversations = conversations.stream() .filter(c -> c.getNotificationChannel() != null @@ -252,7 +250,11 @@ public class PeopleSpaceUtils { } // If tile is null, we need to retrieve from persisted storage. - if (DEBUG) Log.d(TAG, "Retrieving from storage after reboots"); + if (DEBUG) { + Log.d(TAG, + "Retrieving from storage after reboots: " + shortcutId + " user: " + userId + + " pkg: " + pkg); + } LauncherApps launcherApps = context.getSystemService(LauncherApps.class); ConversationChannel channel = peopleManager.getConversation(pkg, userId, shortcutId); if (channel == null) { @@ -384,7 +386,7 @@ public class PeopleSpaceUtils { PeopleSpaceTile tile, Map<String, NotificationEntry> visibleNotifications) { String shortcutId = tile.getId(); String packageName = tile.getPackageName(); - int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier(); + int userId = getUserId(tile); String key = getKey(shortcutId, packageName, userId); if (!visibleNotifications.containsKey(key)) { if (DEBUG) Log.d(TAG, "No existing notifications for key:" + key); @@ -641,7 +643,8 @@ public class PeopleSpaceUtils { activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, tile.getId()); activityIntent.putExtra( PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME, tile.getPackageName()); - activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_UID, tile.getUid()); + activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE, + tile.getUserHandle()); views.setOnClickPendingIntent(R.id.item, PendingIntent.getActivity( context, appWidgetId, @@ -788,7 +791,6 @@ public class PeopleSpaceUtils { Log.i(TAG, "ConversationChannel is null"); return null; } - PeopleSpaceTile tile = new PeopleSpaceTile.Builder(channel, launcherApps).build(); if (!PeopleSpaceUtils.shouldKeepConversation(tile)) { Log.i(TAG, "PeopleSpaceTile is not valid"); @@ -1069,11 +1071,6 @@ public class PeopleSpaceUtils { /** Returns the userId associated with a {@link PeopleSpaceTile} */ public static int getUserId(PeopleSpaceTile tile) { - return getUserHandle(tile).getIdentifier(); - } - - /** Returns the {@link UserHandle} associated with a {@link PeopleSpaceTile} */ - public static UserHandle getUserHandle(PeopleSpaceTile tile) { - return UserHandle.getUserHandleForUid(tile.getUid()); + return tile.getUserHandle().getIdentifier(); } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java index 358f3113ef88..13e30f920f42 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java @@ -41,7 +41,8 @@ public class LaunchConversationActivity extends Activity { Intent intent = getIntent(); String tileId = intent.getStringExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID); String packageName = intent.getStringExtra(PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME); - int uid = intent.getIntExtra(PeopleSpaceWidgetProvider.EXTRA_UID, 0); + UserHandle userHandle = intent.getParcelableExtra( + PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE); if (tileId != null && !tileId.isEmpty()) { if (DEBUG) { @@ -52,7 +53,7 @@ public class LaunchConversationActivity extends Activity { LauncherApps launcherApps = getApplicationContext().getSystemService(LauncherApps.class); launcherApps.startShortcut( - packageName, tileId, null, null, UserHandle.getUserHandleForUid(uid)); + packageName, tileId, null, null, userHandle); } catch (Exception e) { Log.e(TAG, "Exception starting shortcut:" + e); } diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java index 3d1055fdece2..90baf56e0137 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java @@ -48,7 +48,7 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider { public static final String EXTRA_TILE_ID = "extra_tile_id"; public static final String EXTRA_PACKAGE_NAME = "extra_package_name"; - public static final String EXTRA_UID = "extra_uid"; + public static final String EXTRA_USER_HANDLE = "extra_user_handle"; public UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java index 80794cb64883..050352292b38 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java @@ -123,7 +123,8 @@ public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.R fillInIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, tile.getId()); fillInIntent.putExtra( PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME, tile.getPackageName()); - fillInIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_UID, tile.getUid()); + fillInIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE, + tile.getUserHandle()); personView.setOnClickFillInIntent(R.id.item, fillInIntent); } catch (Exception e) { Log.e(TAG, "Couldn't retrieve shortcut information", e); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index c8edaec98ee4..fe76668ab68b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -29,7 +29,6 @@ import com.android.systemui.qs.QSPanel.QSTileLayout; import com.android.systemui.qs.TouchAnimator.Builder; import com.android.systemui.qs.TouchAnimator.Listener; import com.android.systemui.qs.dagger.QSScope; -import com.android.systemui.qs.tileimpl.QSTileBaseView; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; @@ -56,7 +55,8 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha private final ArrayList<View> mAllViews = new ArrayList<>(); /** * List of {@link View}s representing Quick Settings that are being animated from the quick QS - * position to the normal QS panel. + * position to the normal QS panel. These views will only show once the animation is complete, + * to prevent overlapping of semi transparent views */ private final ArrayList<View> mQuickQsViews = new ArrayList<>(); private final QuickQSPanel mQuickQsPanel; @@ -233,7 +233,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha // Quick tiles. QSTileView quickTileView = mQuickQSPanelController.getTileView(tile); if (quickTileView == null) continue; - View qqsBgCircle = ((QSTileBaseView) quickTileView).getBgCircle(); getRelativePosition(loc1, quickTileView.getIcon().getIconView(), view); getRelativePosition(loc2, tileIcon, view); @@ -255,11 +254,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0); translationYBuilder.addFloat(tileView, "translationY", -yDiff, 0); - if (mFeatureFlags.isQSLabelsEnabled()) { - firstPageBuilder.addFloat(qqsBgCircle, "alpha", 1, 1, 0); - mAllViews.add(qqsBgCircle); - } - } else { // These tiles disappear when expanding firstPageBuilder.addFloat(quickTileView, "alpha", 1, 0); translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff); @@ -271,7 +265,11 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha translationX); } - mQuickQsViews.add(tileView.getIconWithBackground()); + if (mFeatureFlags.isQSLabelsEnabled()) { + mQuickQsViews.add(tileView); + } else { + mQuickQsViews.add(tileView.getIconWithBackground()); + } mAllViews.add(tileView.getIcon()); mAllViews.add(quickTileView); } else if (mFullRows && isIconInAnimatedRow(count)) { @@ -362,7 +360,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha if(view == parent || view == null) return; // Ignore tile pages as they can have some offset we don't want to take into account in // RTL. - if (!(view instanceof PagedTileLayout.TilePage || view instanceof SideLabelTileLayout)) { + if (!isAPage(view)) { loc1[0] += view.getLeft(); loc1[1] += view.getTop(); } @@ -374,6 +372,16 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha getRelativePositionInt(loc1, (View) view.getParent(), parent); } + // Returns true if the view is a possible page in PagedTileLayout + private boolean isAPage(View view) { + if (view instanceof PagedTileLayout.TilePage) { + return true; + } else if (view instanceof SideLabelTileLayout) { + return !(view instanceof QuickQSPanel.QQSSideLabelTileLayout); + } + return false; + } + public void setPosition(float position) { if (mNeedsAnimatorUpdate) { updateAnimators(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java index 16e519657a41..2bea72cc0c7e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java @@ -16,6 +16,8 @@ package com.android.systemui.qs; +import static com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED; + import android.content.ClipData; import android.content.ClipboardManager; import android.content.Intent; @@ -41,6 +43,7 @@ import com.android.systemui.tuner.TunerService; import com.android.systemui.util.ViewController; import javax.inject.Inject; +import javax.inject.Named; /** * Controller for {@link QSFooterView}. @@ -63,6 +66,8 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme private final View mEdit; private final MultiUserSwitch mMultiUserSwitch; private final PageIndicator mPageIndicator; + private final View mPowerMenuLite; + private final boolean mShowPMLiteButton; private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener = new UserInfoController.OnUserInfoChangedListener() { @@ -123,7 +128,8 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme DeviceProvisionedController deviceProvisionedController, UserTracker userTracker, QSPanelController qsPanelController, QSDetailDisplayer qsDetailDisplayer, QuickQSPanelController quickQSPanelController, - TunerService tunerService, MetricsLogger metricsLogger) { + TunerService tunerService, MetricsLogger metricsLogger, + @Named(PM_LITE_ENABLED) boolean showPMLiteButton) { super(view); mUserManager = userManager; mUserInfoController = userInfoController; @@ -141,10 +147,15 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme mEdit = mView.findViewById(android.R.id.edit); mMultiUserSwitch = mView.findViewById(R.id.multi_user_switch); mPageIndicator = mView.findViewById(R.id.footer_page_indicator); + mPowerMenuLite = mView.findViewById(R.id.pm_lite); + mShowPMLiteButton = showPMLiteButton; } @Override protected void onViewAttached() { + if (mShowPMLiteButton) { + mPowerMenuLite.setVisibility(View.VISIBLE); + } mView.addOnLayoutChangeListener( (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> mView.updateAnimator( diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 91ae571d1cfb..05633071be86 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -112,7 +112,7 @@ public class QSPanel extends LinearLayout implements Tunable { private int mMediaTotalBottomMargin; private int mFooterMarginStartHorizontal; private Consumer<Boolean> mMediaVisibilityChangedListener; - private boolean mSideLabels; + protected boolean mSideLabels; public QSPanel(Context context, AttributeSet attrs) { super(context, attrs); @@ -201,16 +201,20 @@ public class QSPanel extends LinearLayout implements Tunable { mFooterPageIndicator.setNumPages(((PagedTileLayout) mTileLayout).getNumPages()); } - // Allow the UI to be as big as it want's to, we're in a scroll view - int newHeight = 10000; - int availableHeight = MeasureSpec.getSize(heightMeasureSpec); - int excessHeight = newHeight - availableHeight; - // Measure with EXACTLY. That way, The content will only use excess height and will - // be measured last, after other views and padding is accounted for. This only - // works because our Layouts in here remeasure themselves with the exact content - // height. - heightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY); - ((PagedTileLayout) mTileLayout).setExcessHeight(excessHeight); + // In landscape, mTileLayout's parent is not the panel but a view that contains the + // tile layout and the media controls. + if (((View) mTileLayout).getParent() == this) { + // Allow the UI to be as big as it want's to, we're in a scroll view + int newHeight = 10000; + int availableHeight = MeasureSpec.getSize(heightMeasureSpec); + int excessHeight = newHeight - availableHeight; + // Measure with EXACTLY. That way, The content will only use excess height and will + // be measured last, after other views and padding is accounted for. This only + // works because our Layouts in here remeasure themselves with the exact content + // height. + heightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY); + ((PagedTileLayout) mTileLayout).setExcessHeight(excessHeight); + } } super.onMeasure(widthMeasureSpec, heightMeasureSpec); @@ -758,7 +762,7 @@ public class QSPanel extends LinearLayout implements Tunable { // Let's use 3 columns to match the current layout int columns; if (mSideLabels) { - columns = horizontal ? 1 : 2; + columns = horizontal ? 2 : 4; } else { columns = horizontal ? 3 : TileLayout.NO_MAX_COLUMNS; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java index fcb35e2040ea..d60801ef2d03 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java @@ -18,7 +18,6 @@ package com.android.systemui.qs; import static com.android.systemui.media.dagger.MediaModule.QS_PANEL; import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS; -import static com.android.systemui.qs.dagger.QSFlagsModule.QS_LABELS_FLAG; import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER; import android.annotation.NonNull; @@ -66,7 +65,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { private BrightnessMirrorController mBrightnessMirrorController; private boolean mGridContentVisible = true; - private boolean mQsLabelsFlag; private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener = new QSPanel.OnConfigurationChangedListener() { @@ -93,7 +91,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory, BrightnessSlider.Factory brightnessSliderFactory, - @Named(QS_LABELS_FLAG) boolean qsLabelsFlag, FeatureFlags featureFlags) { super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger, uiEventLogger, qsLogger, dumpManager, featureFlags); @@ -108,9 +105,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { mView.setBrightnessView(mBrightnessSlider.getRootView()); mBrightnessController = brightnessControllerFactory.create(mBrightnessSlider); - - mQsLabelsFlag = qsLabelsFlag; - mSideLabels = qsLabelsFlag; } @Override @@ -329,7 +323,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { @Override public void onTuningChanged(String key, String newValue) { if (QS_REMOVE_LABELS.equals(key)) { - if (!mQsLabelsFlag) return; + if (!mQSLabelFlag) return; boolean newShowLabels = newValue == null || "0".equals(newValue); if (mShowLabels == newShowLabels) return; mShowLabels = newShowLabels; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index 9426e7122c1c..f1174fbe7aef 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -75,7 +75,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr private final QSHost.Callback mQSHostCallback = this::setTiles; protected boolean mShowLabels = true; - protected boolean mSideLabels; + protected boolean mQSLabelFlag; private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener = new QSPanel.OnConfigurationChangedListener() { @@ -118,11 +118,12 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr mQSLogger = qsLogger; mDumpManager = dumpManager; mFeatureFlags = featureFlags; + mQSLabelFlag = featureFlags.isQSLabelsEnabled(); } @Override protected void onInit() { - mView.initialize(mSideLabels); + mView.initialize(mQSLabelFlag); mQSLogger.logAllTilesChangeListening(mView.isListening(), mView.getDumpableTag(), ""); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index a29ac3bb77e9..9b66b59c06df 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -69,12 +69,22 @@ public class QuickQSPanel extends QSPanel { @Override public TileLayout createRegularTileLayout() { - return new QuickQSPanel.HeaderTileLayout(mContext); + if (mSideLabels) { + return new QQSSideLabelTileLayout(mContext); + } else { + return new QuickQSPanel.HeaderTileLayout(mContext); + } } @Override protected QSTileLayout createHorizontalTileLayout() { - return new DoubleLineTileLayout(mContext); + if (mSideLabels) { + TileLayout t = createRegularTileLayout(); + t.setMaxColumns(2); + return t; + } else { + return new DoubleLineTileLayout(mContext); + } } @Override @@ -331,4 +341,38 @@ public class QuickQSPanel extends QSPanel { } } } + + static class QQSSideLabelTileLayout extends SideLabelTileLayout { + QQSSideLabelTileLayout(Context context) { + super(context, null); + setClipChildren(false); + setClipToPadding(false); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.WRAP_CONTENT); + lp.gravity = Gravity.CENTER_HORIZONTAL; + setLayoutParams(lp); + setMaxColumns(4); + } + + @Override + public boolean updateResources() { + boolean b = super.updateResources(); + mMaxAllowedRows = 2; + return b; + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + updateResources(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + // Make sure to always use the correct number of rows. As it's determined by the + // columns, just use as many as needed. + updateMaxRows(10000, mRecords.size()); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java index 383e932a6955..671f8f7dd2d1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java @@ -17,7 +17,6 @@ package com.android.systemui.qs; import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL; -import static com.android.systemui.qs.dagger.QSFlagsModule.QS_LABELS_FLAG; import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER; import com.android.internal.logging.MetricsLogger; @@ -42,8 +41,6 @@ import javax.inject.Named; @QSScope public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> { - private boolean mUseSideLabels; - private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener = newConfig -> { int newMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns); @@ -58,12 +55,10 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer, @Named(QUICK_QS_PANEL) MediaHost mediaHost, MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger, - DumpManager dumpManager, @Named(QS_LABELS_FLAG) boolean qsLabelsFlag, - FeatureFlags featureFlags + DumpManager dumpManager, FeatureFlags featureFlags ) { super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger, uiEventLogger, qsLogger, dumpManager, featureFlags); - mUseSideLabels = qsLabelsFlag; } @Override @@ -104,19 +99,7 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> break; } } - if (mUseSideLabels) { - List<QSTile> newTiles = new ArrayList<>(); - for (int i = 0; i < tiles.size(); i += 2) { - newTiles.add(tiles.get(i)); - } - for (int i = 1; i < tiles.size(); i += 2) { - newTiles.add(tiles.get(i)); - } - super.setTiles(newTiles, true); - - } else { - super.setTiles(tiles, true); - } + super.setTiles(tiles, !mQSLabelFlag); } /** */ diff --git a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt index 74a7ac1cb6dd..4de4a78e9e8a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt @@ -20,11 +20,13 @@ import android.content.Context import android.util.AttributeSet import com.android.systemui.R -open class SideLabelTileLayout(context: Context, attrs: AttributeSet) : TileLayout(context, attrs) { +open class SideLabelTileLayout( + context: Context, + attrs: AttributeSet? +) : TileLayout(context, attrs) { override fun updateResources(): Boolean { return super.updateResources().also { - mResourceColumns = 2 mMaxAllowedRows = 4 mCellMarginHorizontal = (mCellMarginHorizontal * 1.2).toInt() mCellMarginVertical = mCellMarginHorizontal diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java index d559e07f3ff6..14cbf980df23 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java @@ -124,6 +124,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout { public boolean updateResources() { final Resources res = mContext.getResources(); mResourceColumns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns)); + updateColumns(); mMaxCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height); mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal); mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical); diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java index 048fdc3a0e5a..7a91421b00a1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java @@ -75,6 +75,8 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta private static final int ACTION_ADD = 1; private static final int ACTION_MOVE = 2; + private static final int NUM_COLUMNS_ID = R.integer.quick_settings_edit_num_columns; + private final Context mContext; private final Handler mHandler = new Handler(); @@ -87,6 +89,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta private int mEditIndex; private int mTileDividerIndex; private int mFocusIndex; + private boolean mNeedsFocus; private List<String> mCurrentSpecs; private List<TileInfo> mOtherTiles; @@ -109,7 +112,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta mDecoration = new TileItemDecoration(context); mMarginDecoration = new MarginTileDecoration(); mMinNumTiles = context.getResources().getInteger(R.integer.quick_settings_min_num_tiles); - mNumColumns = context.getResources().getInteger(R.integer.quick_settings_num_columns); + mNumColumns = context.getResources().getInteger(NUM_COLUMNS_ID); mAccessibilityDelegate = new TileAdapterDelegate(); } @@ -129,7 +132,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta * @return {@code true} if the number of columns changed, {@code false} otherwise */ public boolean updateNumColumns() { - int numColumns = mContext.getResources().getInteger(R.integer.quick_settings_num_columns); + int numColumns = mContext.getResources().getInteger(NUM_COLUMNS_ID); if (numColumns != mNumColumns) { mNumColumns = numColumns; return true; diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java index 35a8257bd5a7..10192bc20df9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java @@ -21,6 +21,7 @@ import android.hardware.display.ColorDisplayManager; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.FeatureFlags; +import com.android.systemui.util.settings.GlobalSettings; import javax.inject.Named; @@ -31,6 +32,8 @@ import dagger.Provides; public interface QSFlagsModule { String QS_LABELS_FLAG = "qs_labels_flag"; String RBC_AVAILABLE = "rbc_available"; + String PM_LITE_ENABLED = "pm_lite"; + String PM_LITE_SETTING = "sysui_pm_lite"; @Provides @SysUISingleton @@ -46,4 +49,11 @@ public interface QSFlagsModule { static boolean isReduceBrightColorsAvailable(Context context) { return ColorDisplayManager.isReduceBrightColorsAvailable(context); } + + @Provides + @SysUISingleton + @Named(PM_LITE_ENABLED) + static boolean isPMLiteEnabled(FeatureFlags featureFlags, GlobalSettings globalSettings) { + return featureFlags.isPMLiteEnabled() && globalSettings.getInt(PM_LITE_SETTING, 0) != 0; + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java index a699e2ec7cfc..424aafac1f7a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java @@ -105,22 +105,25 @@ public class QSTileView extends QSTileBaseView { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + mLabel.setSingleLine(false); super.onMeasure(widthMeasureSpec, heightMeasureSpec); - // Remeasure view if the primary label requires more then 2 lines or the secondary label - // text will be cut off. - if (mLabel.getLineCount() > mMaxLabelLines || !TextUtils.isEmpty(mSecondLine.getText()) - && mSecondLine.getLineHeight() > mSecondLine.getHeight()) { - if (!mLabel.isSingleLine()) { - mLabel.setSingleLine(); - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - } else { - if (mLabel.isSingleLine()) { - mLabel.setSingleLine(false); - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } + // Remeasure view if the primary label requires more than mMaxLabelLines lines or the + // secondary label text will be cut off. + if (shouldLabelBeSingleLine()) { + mLabel.setSingleLine(); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + } + + private boolean shouldLabelBeSingleLine() { + if (mLabel.getLineCount() > mMaxLabelLines) { + return true; + } else if (!TextUtils.isEmpty(mSecondLine.getText()) + && mLabel.getLineCount() > mMaxLabelLines - 1) { + return true; } + return false; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt index dc81b702021f..c98de8cb8fab 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt @@ -24,8 +24,8 @@ import android.graphics.drawable.PaintDrawable import android.graphics.drawable.RippleDrawable import android.service.quicksettings.Tile.STATE_ACTIVE import android.view.Gravity -import android.view.View import android.widget.LinearLayout +import android.widget.RelativeLayout import com.android.systemui.R import com.android.systemui.plugins.qs.QSIconView import com.android.systemui.plugins.qs.QSTile @@ -37,7 +37,6 @@ class QSTileViewHorizontal( ) : QSTileView(context, icon, false) { private var paintDrawable: PaintDrawable? = null - private var divider: View? = null init { orientation = HORIZONTAL @@ -49,7 +48,12 @@ class QSTileViewHorizontal( override fun createLabel() { super.createLabel() - findViewById<LinearLayout>(R.id.label_group)?.gravity = Gravity.START + findViewById<LinearLayout>(R.id.label_group)?.apply { + gravity = Gravity.START + (layoutParams as? RelativeLayout.LayoutParams)?.apply { + removeRule(RelativeLayout.ALIGN_PARENT_TOP) + } + } mLabel.gravity = Gravity.START mLabel.textDirection = TEXT_DIRECTION_LOCALE mSecondLine.gravity = Gravity.START @@ -57,7 +61,7 @@ class QSTileViewHorizontal( val padding = context.resources.getDimensionPixelSize(R.dimen.qs_tile_side_label_padding) mLabelContainer.setPaddingRelative(0, padding, padding, padding) (mLabelContainer.layoutParams as LayoutParams).gravity = - Gravity.CENTER_VERTICAL or Gravity.START + Gravity.CENTER_VERTICAL or Gravity.START } override fun updateRippleSize() { @@ -93,7 +97,6 @@ class QSTileViewHorizontal( paintDrawable?.setTint(getCircleColor(state.state)) mSecondLine.setTextColor(mLabel.textColors) mLabelContainer.background = null - divider?.backgroundTintList = mLabel.textColors } override fun handleExpand(dualTarget: Boolean) {} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java index 3841daca7ebe..70287cd37d01 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java @@ -16,10 +16,13 @@ package com.android.systemui.qs.tiles; -import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA; +import static android.content.pm.PackageManager.FEATURE_CAMERA_TOGGLE; +import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; import static com.android.systemui.DejankUtils.whitelistIpcs; +import android.hardware.SensorPrivacyManager; +import android.hardware.SensorPrivacyManager.Sensors.Sensor; import android.os.Handler; import android.os.Looper; import android.provider.DeviceConfig; @@ -58,8 +61,8 @@ public class CameraToggleTile extends SensorPrivacyToggleTile { @Override public boolean isAvailable() { - return /*getHost().getContext().getPackageManager().hasSystemFeature(FEATURE_CAMERA_TOGGLE) - && */whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + return getHost().getContext().getPackageManager().hasSystemFeature(FEATURE_CAMERA_TOGGLE) + && whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, "camera_toggle_enabled", false)); } @@ -75,7 +78,7 @@ public class CameraToggleTile extends SensorPrivacyToggleTile { } @Override - public int getSensorId() { + public @Sensor int getSensorId() { return CAMERA; } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java index 2f0071a1f198..e9b712df2154 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java @@ -16,10 +16,13 @@ package com.android.systemui.qs.tiles; -import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE; +import static android.content.pm.PackageManager.FEATURE_MICROPHONE_TOGGLE; +import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE; import static com.android.systemui.DejankUtils.whitelistIpcs; +import android.hardware.SensorPrivacyManager; +import android.hardware.SensorPrivacyManager.Sensors.Sensor; import android.os.Handler; import android.os.Looper; import android.provider.DeviceConfig; @@ -58,7 +61,9 @@ public class MicrophoneToggleTile extends SensorPrivacyToggleTile { @Override public boolean isAvailable() { - return whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + return getHost().getContext().getPackageManager() + .hasSystemFeature(FEATURE_MICROPHONE_TOGGLE) + && whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, "mic_toggle_enabled", false)); } @@ -74,7 +79,7 @@ public class MicrophoneToggleTile extends SensorPrivacyToggleTile { } @Override - public int getSensorId() { + public @Sensor int getSensorId() { return MICROPHONE; } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java index 00703e7f8403..0c582bdbe12f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java @@ -17,7 +17,7 @@ package com.android.systemui.qs.tiles; import android.content.Intent; -import android.hardware.SensorPrivacyManager.IndividualSensor; +import android.hardware.SensorPrivacyManager.Sensors.Sensor; import android.os.Handler; import android.os.Looper; import android.service.quicksettings.Tile; @@ -49,7 +49,7 @@ public abstract class SensorPrivacyToggleTile extends QSTileImpl<QSTile.BooleanS /** * @return Id of the sensor that will be toggled */ - public abstract @IndividualSensor int getSensorId(); + public abstract @Sensor int getSensorId(); /** * @return icon for the QS tile diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS b/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS new file mode 100644 index 000000000000..9b3e386bc0d8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS @@ -0,0 +1,12 @@ +# Scroll Capture (Long Screenshots) +# Bug component: 801322 +# +# Referenced by: +# +# core/java/src/android/view/OWNERS +# core/java/src/com/android/internal/view/OWNERS +# core/tests/coretests/src/android/view/OWNERS +# core/tests/coretests/src/com/android/internal/view/OWNERS + +mrcasey@google.com +mrenouf@google.com diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 131fde6b4b56..805ac7cf1ec9 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -325,6 +325,7 @@ public class ScreenshotController { attachWindow(); mWindow.setContentView(mScreenshotView); + mScreenshotView.requestApplyInsets(); mScreenshotView.takePartialScreenshot( rect -> takeScreenshotInternal(finisher, rect)); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java index dc639dce4951..70be601c3e09 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java @@ -21,24 +21,25 @@ import static com.android.systemui.screenshot.LogConfig.DEBUG_SCROLL; import static java.lang.Math.min; import static java.util.Objects.requireNonNull; +import android.annotation.BinderThread; import android.annotation.UiContext; import android.app.ActivityTaskManager; import android.content.Context; import android.graphics.PixelFormat; -import android.graphics.Point; import android.graphics.Rect; import android.hardware.HardwareBuffer; import android.media.Image; import android.media.ImageReader; import android.os.IBinder; +import android.os.ICancellationSignal; import android.os.RemoteException; import android.util.Log; import android.view.IScrollCaptureCallbacks; import android.view.IScrollCaptureConnection; import android.view.IWindowManager; +import android.view.ScrollCaptureResponse; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.view.ScrollCaptureViewSupport; import java.util.function.Consumer; @@ -62,16 +63,19 @@ public class ScrollCaptureClient { */ public interface Connection { /** - * Session start should be deferred until UI is active because of resource allocation and - * potential visible side effects in the target window. - * + * Start a session. + * @param sessionConsumer listener to receive the session once active * @param maxPages the capture buffer size expressed as a multiple of the content height */ + // TODO ListenableFuture void start(Consumer<Session> sessionConsumer, float maxPages); /** - * Close the connection. + * Close the connection. Must end capture if started to avoid potential unwanted visual + * artifacts. + * + * @see Session#end(Runnable) */ void close(); } @@ -119,6 +123,7 @@ public class ScrollCaptureClient { * @param top the top (y) position of the tile to capture, in content rect space * @param consumer listener to be informed of the result */ + // TODO ListenableFuture void requestTile(int top, Consumer<CaptureResult> consumer); /** @@ -129,16 +134,31 @@ public class ScrollCaptureClient { */ int getMaxTiles(); + /** + * @return the height of each image tile + */ int getTileHeight(); + /** + * @return the height of scrollable content being captured + */ int getPageHeight(); + /** + * @return the width of the scrollable page + */ int getPageWidth(); /** + * @return the bounds on screen of the window being captured. + */ + Rect getWindowBounds(); + + /** * End the capture session, return the target app to original state. The listener * will be called when the target app is ready to before visible and interactive. */ + // TODO ListenableFuture void end(Runnable listener); } @@ -185,13 +205,13 @@ public class ScrollCaptureClient { + ", taskId=" + taskId + ", consumer=" + consumer + ")"); } mWindowManagerService.requestScrollCapture(displayId, mHostWindowToken, taskId, - new ControllerCallbacks(consumer)); + new ClientCallbacks(consumer)); } catch (RemoteException e) { Log.e(TAG, "Ignored remote exception", e); } } - private static class ControllerCallbacks extends IScrollCaptureCallbacks.Stub implements + private static class ClientCallbacks extends IScrollCaptureCallbacks.Stub implements Connection, Session, IBinder.DeathRecipient { private IScrollCaptureConnection mConnection; @@ -206,46 +226,62 @@ public class ScrollCaptureClient { private int mTileWidth; private Rect mRequestRect; private boolean mStarted; + + private ICancellationSignal mCancellationSignal; + private Rect mWindowBounds; + private Rect mBoundsInWindow; private int mMaxTiles; - private ControllerCallbacks(Consumer<Connection> connectionConsumer) { + private ClientCallbacks(Consumer<Connection> connectionConsumer) { mConnectionConsumer = connectionConsumer; } - // IScrollCaptureCallbacks - + @BinderThread @Override - public void onConnected(IScrollCaptureConnection connection, Rect scrollBounds, - Point positionInWindow) throws RemoteException { + public void onScrollCaptureResponse(ScrollCaptureResponse response) throws RemoteException { if (DEBUG_SCROLL) { - Log.d(TAG, "onConnected(connection=" + connection + ", scrollBounds=" + scrollBounds - + ", positionInWindow=" + positionInWindow + ")"); + Log.d(TAG, "onScrollCaptureResponse(response=" + response + ")"); } - mConnection = connection; - mConnection.asBinder().linkToDeath(this, 0); - mScrollBounds = scrollBounds; - mConnectionConsumer.accept(this); - mConnectionConsumer = null; - - int pxPerPage = mScrollBounds.width() * mScrollBounds.height(); - int pxPerTile = min(TILE_SIZE_PX_MAX, (pxPerPage / TILES_PER_PAGE)); - mTileWidth = mScrollBounds.width(); - mTileHeight = pxPerTile / mScrollBounds.width(); - if (DEBUG_SCROLL) { - Log.d(TAG, "scrollBounds: " + mScrollBounds); - Log.d(TAG, "tile dimen: " + mTileWidth + "x" + mTileHeight); + if (response.isConnected()) { + mConnection = response.getConnection(); + mConnection.asBinder().linkToDeath(this, 0); + mWindowBounds = response.getWindowBounds(); + mBoundsInWindow = response.getBoundsInWindow(); + + int pxPerPage = mBoundsInWindow.width() * mBoundsInWindow.height(); + int pxPerTile = min(TILE_SIZE_PX_MAX, (pxPerPage / TILES_PER_PAGE)); + mTileWidth = mBoundsInWindow.width(); + mTileHeight = pxPerTile / mBoundsInWindow.width(); + if (DEBUG_SCROLL) { + Log.d(TAG, "boundsInWindow: " + mBoundsInWindow); + Log.d(TAG, "tile size: " + mTileWidth + "x" + mTileHeight); + Log.d(TAG, "maxHeight: " + (mMaxTiles * mTileHeight) + "px"); + } + mConnectionConsumer.accept(this); } + mConnectionConsumer = null; } @Override - public void onUnavailable() throws RemoteException { + public void start(Consumer<Session> sessionConsumer, float maxPages) { if (DEBUG_SCROLL) { - Log.d(TAG, "onUnavailable"); + Log.d(TAG, "start(sessionConsumer=" + sessionConsumer + "," + + " maxPages=" + maxPages + ")"); + } + mMaxTiles = (int) Math.ceil(maxPages * TILES_PER_PAGE); + mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888, + mMaxTiles, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE); + mSessionConsumer = sessionConsumer; + + try { + mCancellationSignal = mConnection.startCapture(mReader.getSurface()); + mStarted = true; + } catch (RemoteException e) { + Log.w(TAG, "Failed to start", e); } - // The targeted app does not support scroll capture - // or the window could not be found... etc etc. } + @BinderThread @Override public void onCaptureStarted() { if (DEBUG_SCROLL) { @@ -256,13 +292,25 @@ public class ScrollCaptureClient { } @Override - public void onCaptureBufferSent(long frameNumber, Rect contentArea) { - Image image = null; - if (frameNumber != ScrollCaptureViewSupport.NO_FRAME_PRODUCED) { - image = mReader.acquireNextImage(); + public void requestTile(int top, Consumer<CaptureResult> consumer) { + if (DEBUG_SCROLL) { + Log.d(TAG, "requestTile(top=" + top + ", consumer=" + consumer + ")"); + } + cancelPendingRequest(); + mRequestRect = new Rect(0, top, mTileWidth, top + mTileHeight); + mResultConsumer = consumer; + try { + mCancellationSignal = mConnection.requestImage(mRequestRect); + } catch (RemoteException e) { + Log.e(TAG, "Caught remote exception from requestImage", e); } + } + + @Override + public void onImageRequestCompleted(int flags, Rect contentArea) { + Image image = mReader.acquireLatestImage(); if (DEBUG_SCROLL) { - Log.d(TAG, "onCaptureBufferSent(frameNumber=" + frameNumber + Log.d(TAG, "onCaptureBufferSent(flags=" + flags + ", contentArea=" + contentArea + ") image=" + image); } // Save and clear first, since the consumer will likely request the next @@ -273,17 +321,48 @@ public class ScrollCaptureClient { } @Override - public void onConnectionClosed() { + public void end(Runnable listener) { if (DEBUG_SCROLL) { - Log.d(TAG, "onConnectionClosed()"); + Log.d(TAG, "end(listener=" + listener + ")"); } - disconnect(); + if (mStarted) { + mShutdownListener = listener; + try { + // listener called from onConnectionClosed callback + mConnection.endCapture(); + } catch (RemoteException e) { + Log.d(TAG, "Ignored exception from endCapture()", e); + disconnect(); + listener.run(); + } + } else { + disconnect(); + listener.run(); + } + } + + @BinderThread + @Override + public void onCaptureEnded() { + close(); if (mShutdownListener != null) { mShutdownListener.run(); mShutdownListener = null; } } + @Override + public void close() { + if (mConnection != null) { + try { + mConnection.close(); + } catch (RemoteException e) { + /* ignore */ + } + disconnect(); + } + } + // Misc private void disconnect() { @@ -293,63 +372,25 @@ public class ScrollCaptureClient { mConnection = null; } - // ScrollCaptureController.Connection - - @Override - public void start(Consumer<Session> sessionConsumer, float maxPages) { - if (DEBUG_SCROLL) { - Log.d(TAG, "start(sessionConsumer=" + sessionConsumer + "," - + " maxPages=" + maxPages + ")" - + " [maxHeight: " + (mMaxTiles * mTileHeight) + "px]"); - } - mMaxTiles = (int) Math.ceil(maxPages * TILES_PER_PAGE); - mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888, - mMaxTiles, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE); - mSessionConsumer = sessionConsumer; - try { - mConnection.startCapture(mReader.getSurface()); - mStarted = true; - } catch (RemoteException e) { - Log.w(TAG, "Failed to start", e); - } - } - - @Override - public void close() { - end(null); - } - - // ScrollCaptureController.Session - + /** + * The process hosting the window went away abruptly! + */ @Override - public void end(Runnable listener) { + public void binderDied() { if (DEBUG_SCROLL) { - Log.d(TAG, "end(listener=" + listener + ")"); - } - if (mStarted) { - mShutdownListener = listener; - try { - // listener called from onConnectionClosed callback - mConnection.endCapture(); - } catch (RemoteException e) { - Log.d(TAG, "Ignored exception from endCapture()", e); - disconnect(); - listener.run(); - } - } else { - disconnect(); - listener.run(); + Log.d(TAG, "binderDied()"); } + disconnect(); } @Override public int getPageHeight() { - return mScrollBounds.height(); + return mBoundsInWindow.height(); } @Override public int getPageWidth() { - return mScrollBounds.width(); + return mBoundsInWindow.width(); } @Override @@ -357,34 +398,24 @@ public class ScrollCaptureClient { return mTileHeight; } - @Override - public int getMaxTiles() { - return mMaxTiles; + public Rect getWindowBounds() { + return new Rect(mWindowBounds); } @Override - public void requestTile(int top, Consumer<CaptureResult> consumer) { - if (DEBUG_SCROLL) { - Log.d(TAG, "requestTile(top=" + top + ", consumer=" + consumer + ")"); - } - mRequestRect = new Rect(0, top, mTileWidth, top + mTileHeight); - mResultConsumer = consumer; - try { - mConnection.requestImage(mRequestRect); - } catch (RemoteException e) { - Log.e(TAG, "Caught remote exception from requestImage", e); - } + public int getMaxTiles() { + return mMaxTiles; } - /** - * The process hosting the window went away abruptly! - */ - @Override - public void binderDied() { - if (DEBUG_SCROLL) { - Log.d(TAG, "binderDied()"); + private void cancelPendingRequest() { + if (mCancellationSignal != null) { + try { + mCancellationSignal.cancel(); + } catch (RemoteException e) { + /* ignore */ + } + mCancellationSignal = null; } - disconnect(); } } } diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt index 9f182e19efaf..658613796498 100644 --- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt @@ -25,8 +25,8 @@ import android.content.pm.PackageManager import android.content.res.Resources import android.hardware.SensorPrivacyManager import android.hardware.SensorPrivacyManager.EXTRA_SENSOR -import android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA -import android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE +import android.hardware.SensorPrivacyManager.Sensors.CAMERA +import android.hardware.SensorPrivacyManager.Sensors.MICROPHONE import android.os.Bundle import android.os.Handler import android.text.Html @@ -81,7 +81,7 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene dismiss() } } - if (!sensorPrivacyManager.isIndividualSensorPrivacyEnabled(sensor)) { + if (!sensorPrivacyManager.isSensorPrivacyEnabled(sensor)) { finish() return } @@ -89,9 +89,9 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene mAlertParams.apply { try { mMessage = Html.fromHtml(getString(when (sensor) { - INDIVIDUAL_SENSOR_MICROPHONE -> + MICROPHONE -> R.string.sensor_privacy_start_use_mic_dialog_content - INDIVIDUAL_SENSOR_CAMERA -> + CAMERA -> R.string.sensor_privacy_start_use_camera_dialog_content else -> Resources.ID_NULL }, packageManager.getApplicationInfo(sensorUsePackageName, 0) @@ -102,9 +102,9 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene } mIconId = when (sensor) { - INDIVIDUAL_SENSOR_MICROPHONE -> + MICROPHONE -> com.android.internal.R.drawable.perm_group_microphone - INDIVIDUAL_SENSOR_CAMERA -> com.android.internal.R.drawable.perm_group_camera + CAMERA -> com.android.internal.R.drawable.perm_group_camera else -> Resources.ID_NULL } @@ -121,7 +121,7 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene override fun onStart() { super.onStart() - sensorPrivacyManager.suppressIndividualSensorPrivacyReminders(sensorUsePackageName, true) + sensorPrivacyManager.suppressSensorPrivacyReminders(sensorUsePackageName, true) unsuppressImmediately = false } @@ -156,11 +156,11 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene if (unsuppressImmediately) { sensorPrivacyManager - .suppressIndividualSensorPrivacyReminders(sensorUsePackageName, false) + .suppressSensorPrivacyReminders(sensorUsePackageName, false) } else { Handler(mainLooper).postDelayed({ sensorPrivacyManager - .suppressIndividualSensorPrivacyReminders(sensorUsePackageName, false) + .suppressSensorPrivacyReminders(sensorUsePackageName, false) }, SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS) } } @@ -170,7 +170,7 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene } private fun disableSensorPrivacy() { - sensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(sensor, false) + sensorPrivacyManager.setSensorPrivacyForProfileGroup(sensor, false) unsuppressImmediately = true setResult(RESULT_OK) } diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java index 0bfc8e5d554b..fea521f15b84 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java @@ -72,8 +72,6 @@ public class BrightnessController implements ToggleSlider.Listener { private static final Uri BRIGHTNESS_FOR_VR_FLOAT_URI = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT); - private final float mMinimumBacklight; - private final float mMaximumBacklight; private final float mDefaultBacklight; private final float mMinimumBacklightForVr; private final float mMaximumBacklightForVr; @@ -314,10 +312,6 @@ public class BrightnessController implements ToggleSlider.Listener { mDisplayId = mContext.getDisplayId(); PowerManager pm = context.getSystemService(PowerManager.class); - mMinimumBacklight = pm.getBrightnessConstraint( - PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM); - mMaximumBacklight = pm.getBrightnessConstraint( - PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM); mDefaultBacklight = mContext.getDisplay().getBrightnessDefault(); mMinimumBacklightForVr = pm.getBrightnessConstraint( PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR); @@ -375,8 +369,8 @@ public class BrightnessController implements ToggleSlider.Listener { metric = mAutomatic ? MetricsEvent.ACTION_BRIGHTNESS_AUTO : MetricsEvent.ACTION_BRIGHTNESS; - minBacklight = mMinimumBacklight; - maxBacklight = mMaximumBacklight; + minBacklight = PowerManager.BRIGHTNESS_MIN; + maxBacklight = PowerManager.BRIGHTNESS_MAX; settingToChange = Settings.System.SCREEN_BRIGHTNESS_FLOAT; } final float valFloat = MathUtils.min(convertGammaToLinearFloat(value, @@ -439,8 +433,8 @@ public class BrightnessController implements ToggleSlider.Listener { min = mMinimumBacklightForVr; max = mMaximumBacklightForVr; } else { - min = mMinimumBacklight; - max = mMaximumBacklight; + min = PowerManager.BRIGHTNESS_MIN; + max = PowerManager.BRIGHTNESS_MAX; } // convertGammaToLinearFloat returns 0-1 if (BrightnessSynchronizer.floatEquals(brightnessValue, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java index cf77e290ebe3..1d59257c9c4f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java @@ -86,4 +86,8 @@ public class FeatureFlags { public boolean isNavigationBarOverlayEnabled() { return mFlagReader.isEnabled(R.bool.flag_navigation_bar_overlay); } + + public boolean isPMLiteEnabled() { + return mFlagReader.isEnabled(R.bool.flag_pm_lite); + } } 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/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java index 414d62092ab2..222735aeb35a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java @@ -49,7 +49,7 @@ public class NotificationCustomViewWrapper extends NotificationViewWrapper { // Custom views will most likely use just white or black as their text color. // We need to scan through and replace these colors by Material NEXT colors. - ensureThemeOnChildren(); + ensureThemeOnChildren(mView); // Let's invert the notification colors when we're in night mode and // the notification background isn't colorized. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java index 301c3726793a..d21ae13a1e01 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java @@ -64,7 +64,7 @@ public class NotificationDecoratedCustomViewWrapper extends NotificationTemplate // Custom views will most likely use just white or black as their text color. // We need to scan through and replace these colors by Material NEXT colors. - ensureThemeOnChildren(); + ensureThemeOnChildren(mWrappedView); if (needsInversion(resolveBackgroundColor(), mWrappedView)) { invertViewLuminosity(mWrappedView); 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..89babf0835c6 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; @@ -58,9 +58,11 @@ public abstract class NotificationViewWrapper implements TransformableView { private final Rect mTmpRect = new Rect(); protected int mBackgroundColor = 0; - private int mLightTextColor; - private int mDarkTextColor; - private int mDefaultTextColor; + private int mMaterialTextColorPrimary; + private int mMaterialTextColorSecondary; + private int mThemedTextColorPrimary; + private int mThemedTextColorSecondary; + 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 +99,8 @@ public abstract class NotificationViewWrapper implements TransformableView { mView = view; mRow = row; onReinflated(); + mAdjustTheme = ctx.getResources().getBoolean( + R.bool.config_adjustThemeOnNotificationCustomViews); } /** @@ -121,15 +125,22 @@ public abstract class NotificationViewWrapper implements TransformableView { mBackgroundColor = backgroundColor; mView.setBackground(new ColorDrawable(Color.TRANSPARENT)); } - 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); + + Context materialTitleContext = new ContextThemeWrapper(mView.getContext(), + com.android.internal.R.style.TextAppearance_Material_Notification_Title); + mMaterialTextColorPrimary = Utils.getColorAttr(materialTitleContext, + com.android.internal.R.attr.textColor).getDefaultColor(); + Context materialContext = new ContextThemeWrapper(mView.getContext(), + com.android.internal.R.style.TextAppearance_Material_Notification); + mMaterialTextColorSecondary = Utils.getColorAttr(materialContext, + com.android.internal.R.attr.textColor).getDefaultColor(); 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); + mThemedTextColorPrimary = Utils.getColorAttr(themedContext, + com.android.internal.R.attr.textColorPrimary).getDefaultColor(); + mThemedTextColorSecondary = Utils.getColorAttr(themedContext, + com.android.internal.R.attr.textColorSecondary).getDefaultColor(); } protected boolean needsInversion(int defaultBackgroundColor, View view) { @@ -207,38 +218,35 @@ public abstract class NotificationViewWrapper implements TransformableView { return false; } - protected void ensureThemeOnChildren() { - if (mView == null) { + protected void ensureThemeOnChildren(View rootView) { + if (!mAdjustTheme || mView == null || rootView == null) { return; } // Notifications with custom backgrounds should not be adjusted if (mBackgroundColor != Color.TRANSPARENT - || getBackgroundColor(mView) != Color.TRANSPARENT) { + || getBackgroundColor(mView) != Color.TRANSPARENT + || getBackgroundColor(rootView) != Color.TRANSPARENT) { return; } // 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(rootView); } - 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 == mMaterialTextColorPrimary) { + textView.setTextColor(mThemedTextColorPrimary); + } else if (foreground == mMaterialTextColorSecondary) { + textView.setTextColor(mThemedTextColorSecondary); + } + } 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/policy/IndividualSensorPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java index a76d08a438f2..7f935d28285f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java @@ -16,17 +16,17 @@ package com.android.systemui.statusbar.policy; -import android.hardware.SensorPrivacyManager.IndividualSensor; +import android.hardware.SensorPrivacyManager.Sensors.Sensor; public interface IndividualSensorPrivacyController extends CallbackController<IndividualSensorPrivacyController.Callback> { void init(); - boolean isSensorBlocked(@IndividualSensor int sensor); + boolean isSensorBlocked(@Sensor int sensor); - void setSensorBlocked(@IndividualSensor int sensor, boolean blocked); + void setSensorBlocked(@Sensor int sensor, boolean blocked); interface Callback { - void onSensorBlockedChanged(@IndividualSensor int sensor, boolean blocked); + void onSensorBlockedChanged(@Sensor int sensor, boolean blocked); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java index 32d15ed41648..295df05797ea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java @@ -16,11 +16,11 @@ package com.android.systemui.statusbar.policy; -import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA; -import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE; +import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; +import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE; import android.hardware.SensorPrivacyManager; -import android.hardware.SensorPrivacyManager.IndividualSensor; +import android.hardware.SensorPrivacyManager.Sensors.Sensor; import android.util.ArraySet; import android.util.SparseBooleanArray; @@ -30,8 +30,7 @@ import java.util.Set; public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPrivacyController { - private static final int[] SENSORS = new int[] {INDIVIDUAL_SENSOR_CAMERA, - INDIVIDUAL_SENSOR_MICROPHONE}; + private static final int[] SENSORS = new int[] {CAMERA, MICROPHONE}; private final @NonNull SensorPrivacyManager mSensorPrivacyManager; private final SparseBooleanArray mState = new SparseBooleanArray(); @@ -48,18 +47,18 @@ public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPr mSensorPrivacyManager.addSensorPrivacyListener(sensor, (enabled) -> onSensorPrivacyChanged(sensor, enabled)); - mState.put(sensor, mSensorPrivacyManager.isIndividualSensorPrivacyEnabled(sensor)); + mState.put(sensor, mSensorPrivacyManager.isSensorPrivacyEnabled(sensor)); } } @Override - public boolean isSensorBlocked(@IndividualSensor int sensor) { + public boolean isSensorBlocked(@Sensor int sensor) { return mState.get(sensor, false); } @Override - public void setSensorBlocked(@IndividualSensor int sensor, boolean blocked) { - mSensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(sensor, blocked); + public void setSensorBlocked(@Sensor int sensor, boolean blocked) { + mSensorPrivacyManager.setSensorPrivacyForProfileGroup(sensor, blocked); } @Override @@ -72,7 +71,7 @@ public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPr mCallbacks.remove(listener); } - private void onSensorPrivacyChanged(@IndividualSensor int sensor, boolean blocked) { + private void onSensorPrivacyChanged(@Sensor int sensor, boolean blocked) { mState.put(sensor, blocked); for (Callback callback : mCallbacks) { diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java index 9effc6728bab..bbb2f1a5259a 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java @@ -161,10 +161,10 @@ public class ThemeOverlayApplier implements Dumpable { void applyCurrentUserOverlays( Map<String, OverlayIdentifier> categoryToPackage, FabricatedOverlay[] pendingCreation, - Set<UserHandle> userHandles) { + int currentUser, + Set<UserHandle> managedProfiles) { // Disable all overlays that have not been specified in the user setting. final Set<String> overlayCategoriesToDisable = new HashSet<>(THEME_CATEGORIES); - overlayCategoriesToDisable.removeAll(categoryToPackage.keySet()); final Set<String> targetPackagesToQuery = overlayCategoriesToDisable.stream() .map(category -> mCategoryToTargetPackage.get(category)) .collect(Collectors.toSet()); @@ -175,6 +175,7 @@ public class ThemeOverlayApplier implements Dumpable { .filter(o -> mTargetPackageToCategories.get(o.targetPackageName).contains(o.category)) .filter(o -> overlayCategoriesToDisable.contains(o.category)) + .filter(o -> !categoryToPackage.containsValue(new OverlayIdentifier(o.packageName))) .filter(o -> o.isEnabled()) .map(o -> new Pair<>(o.category, o.packageName)) .collect(Collectors.toList()); @@ -186,17 +187,18 @@ public class ThemeOverlayApplier implements Dumpable { } } - // Toggle overlays in the order of THEME_CATEGORIES. + for (Pair<String, String> packageToDisable : overlaysToDisable) { + OverlayIdentifier overlayInfo = new OverlayIdentifier(packageToDisable.second); + setEnabled(transaction, overlayInfo, packageToDisable.first, currentUser, + managedProfiles, false); + } + for (String category : THEME_CATEGORIES) { if (categoryToPackage.containsKey(category)) { OverlayIdentifier overlayInfo = categoryToPackage.get(category); - setEnabled(transaction, overlayInfo, category, userHandles, true); + setEnabled(transaction, overlayInfo, category, currentUser, managedProfiles, true); } } - for (Pair<String, String> packageToDisable : overlaysToDisable) { - OverlayIdentifier overlayInfo = new OverlayIdentifier(packageToDisable.second); - setEnabled(transaction, overlayInfo, packageToDisable.first, userHandles, false); - } mExecutor.execute(() -> { try { @@ -213,18 +215,30 @@ public class ThemeOverlayApplier implements Dumpable { } private void setEnabled(OverlayManagerTransaction.Builder transaction, - OverlayIdentifier identifier, String category, Set<UserHandle> handles, - boolean enabled) { + OverlayIdentifier identifier, String category, int currentUser, + Set<UserHandle> managedProfiles, boolean enabled) { if (DEBUG) { Log.d(TAG, "setEnabled: " + identifier.getPackageName() + " category: " + category + ": " + enabled); } - for (UserHandle userHandle : handles) { - transaction.setEnabled(identifier, enabled, userHandle.getIdentifier()); - } - if (!handles.contains(UserHandle.SYSTEM) && SYSTEM_USER_CATEGORIES.contains(category)) { + + transaction.setEnabled(identifier, enabled, currentUser); + if (currentUser != UserHandle.SYSTEM.getIdentifier() + && SYSTEM_USER_CATEGORIES.contains(category)) { transaction.setEnabled(identifier, enabled, UserHandle.SYSTEM.getIdentifier()); } + + // Do not apply Launcher or Theme picker overlays to managed users. Apps are not + // installed in there. + OverlayInfo overlayInfo = mOverlayManager.getOverlayInfo(identifier, UserHandle.SYSTEM); + if (overlayInfo == null || overlayInfo.targetPackageName.equals(mLauncherPackage) + || overlayInfo.targetPackageName.equals(mThemePickerPackage)) { + return; + } + + for (UserHandle userHandle : managedProfiles) { + transaction.setEnabled(identifier, enabled, userHandle.getIdentifier()); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index 028cbd084677..5d028454a417 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -55,14 +55,13 @@ import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.settings.SecureSettings; -import com.google.android.collect.Sets; - import org.json.JSONException; import org.json.JSONObject; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Collection; +import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; @@ -151,7 +150,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { @Override public void onReceive(Context context, Intent intent) { if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added."); - updateThemeOverlays(); + reevaluateSystemTheme(true /* forceReload */); } }, filter, mBgExecutor, UserHandle.ALL); mSecureSettings.registerContentObserverForUser( @@ -163,7 +162,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { int userId) { if (DEBUG) Log.d(TAG, "Overlay changed for user: " + userId); if (ActivityManager.getCurrentUser() == userId) { - updateThemeOverlays(); + reevaluateSystemTheme(true /* forceReload */); } } }, @@ -180,7 +179,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { mLockColors = lockColors; } mSystemColors = systemColor; - reevaluateSystemTheme(); + reevaluateSystemTheme(false /* forceReload */); }); }); if (USE_LOCK_SCREEN_WALLPAPER) { @@ -192,7 +191,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { } // It's possible that the user has a lock screen wallpaper. On this case we'll // end up with different colors after unlocking. - reevaluateSystemTheme(); + reevaluateSystemTheme(false /* forceReload */); } }); } @@ -209,11 +208,11 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { Log.d(TAG, "got new lock colors: " + wallpaperColors + " where: " + which); } } - reevaluateSystemTheme(); + reevaluateSystemTheme(false /* forceReload */); }, null, UserHandle.USER_ALL); } - private void reevaluateSystemTheme() { + private void reevaluateSystemTheme(boolean forceReload) { WallpaperColors currentColors = mKeyguardStateController.isShowing() && mLockColors != null ? mLockColors : mSystemColors; @@ -228,7 +227,8 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { accentCandidate = getAccentColor(currentColors); } - if (mMainWallpaperColor == mainColor && mWallpaperAccentColor == accentCandidate) { + if (mMainWallpaperColor == mainColor && mWallpaperAccentColor == accentCandidate + && !forceReload) { return; } @@ -309,6 +309,16 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { } catch (NumberFormatException e) { Log.w(TAG, "Invalid color definition: " + systemPalette.getPackageName()); } + } else if (!mIsMonetEnabled && systemPalette != null) { + try { + // It's possible that we flipped the flag off and still have a @ColorInt in the + // setting. We need to sanitize the input, otherwise the overlay transaction will + // fail. + Integer.parseInt(systemPalette.getPackageName().toLowerCase(), 16); + categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE); + } catch (NumberFormatException e) { + // This is a package name. All good, let's continue + } } // Same for accent color. @@ -322,6 +332,13 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { } catch (NumberFormatException e) { Log.w(TAG, "Invalid color definition: " + accentPalette.getPackageName()); } + } else if (!mIsMonetEnabled && accentPalette != null) { + try { + Integer.parseInt(accentPalette.getPackageName().toLowerCase(), 16); + categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR); + } catch (NumberFormatException e) { + // This is a package name. All good, let's continue + } } // Compatibility with legacy themes, where full packages were defined, instead of just @@ -337,10 +354,10 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { categoryToPackage.put(OVERLAY_CATEGORY_ACCENT_COLOR, mSecondaryOverlay.getIdentifier()); } - Set<UserHandle> userHandles = Sets.newHashSet(UserHandle.of(currentUser)); + Set<UserHandle> managedProfiles = new HashSet<>(); for (UserInfo userInfo : mUserManager.getEnabledProfiles(currentUser)) { if (userInfo.isManagedProfile()) { - userHandles.add(userInfo.getUserHandle()); + managedProfiles.add(userInfo.getUserHandle()); } } if (DEBUG) { @@ -352,9 +369,10 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { mNeedsOverlayCreation = false; mThemeManager.applyCurrentUserOverlays(categoryToPackage, new FabricatedOverlay[] { mPrimaryOverlay, mSecondaryOverlay, mNeutralOverlay - }, userHandles); + }, currentUser, managedProfiles); } else { - mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, userHandles); + mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, currentUser, + managedProfiles); } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index 5d9465904e3b..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; @@ -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/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java index 97cb8736f01c..2526990dfd40 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java @@ -16,8 +16,8 @@ package com.android.systemui.appops; -import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA; -import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE; +import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; +import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE; import static junit.framework.TestCase.assertFalse; @@ -125,9 +125,9 @@ public class AppOpsControllerTest extends SysuiTestCase { when(mAudioManager.getActiveRecordingConfigurations()) .thenReturn(List.of(mPausedMockRecording)); - when(mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA)) + when(mSensorPrivacyController.isSensorBlocked(CAMERA)) .thenReturn(false); - when(mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA)) + when(mSensorPrivacyController.isSensorBlocked(CAMERA)) .thenReturn(false); mController = new AppOpsControllerImpl( @@ -505,7 +505,7 @@ public class AppOpsControllerTest extends SysuiTestCase { assertFalse(list.get(0).isDisabled()); // Add a camera op, and disable the microphone. The camera op should be the only op returned - mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_MICROPHONE, true); + mController.onSensorBlockedChanged(MICROPHONE, true); mController.onOpActiveChanged( AppOpsManager.OP_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); mTestableLooper.processAllMessages(); @@ -515,7 +515,7 @@ public class AppOpsControllerTest extends SysuiTestCase { // Re enable the microphone, and verify the op returns - mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_MICROPHONE, false); + mController.onSensorBlockedChanged(MICROPHONE, false); mTestableLooper.processAllMessages(); list = mController.getActiveAppOps(); @@ -538,7 +538,7 @@ public class AppOpsControllerTest extends SysuiTestCase { assertFalse(list.get(0).isDisabled()); // Add an audio op, and disable the camera. The audio op should be the only op returned - mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_CAMERA, true); + mController.onSensorBlockedChanged(CAMERA, true); mController.onOpActiveChanged( AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); mTestableLooper.processAllMessages(); @@ -547,7 +547,7 @@ public class AppOpsControllerTest extends SysuiTestCase { assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(0).getCode()); // Re enable the camera, and verify the op returns - mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_CAMERA, false); + mController.onSensorBlockedChanged(CAMERA, false); mTestableLooper.processAllMessages(); list = mController.getActiveAppOps(); 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 c8e939609e87..2a4b41cbfe32 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java @@ -464,7 +464,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { new PeopleSpaceTile .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent()) .setPackageName(PACKAGE_NAME) - .setUid(0) + .setUserHandle(new UserHandle(0)) .build(); PeopleSpaceTile actual = PeopleSpaceUtils .augmentTileFromNotification(mContext, tile, sbn); @@ -482,7 +482,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { new PeopleSpaceTile .Builder(SHORTCUT_ID_3, "userName", ICON, new Intent()) .setPackageName(PACKAGE_NAME) - .setUid(0) + .setUserHandle(new UserHandle(0)) .build(); PeopleSpaceTile actual = PeopleSpaceUtils .augmentTileFromNotification(mContext, tile, sbn); @@ -496,7 +496,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { new PeopleSpaceTile .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent()) .setPackageName(PACKAGE_NAME) - .setUid(0) + .setUserHandle(new UserHandle(0)) .build(); PeopleSpaceTile actual = PeopleSpaceUtils .augmentTileFromVisibleNotifications(mContext, tile, @@ -511,7 +511,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { new PeopleSpaceTile .Builder(SHORTCUT_ID_4, "userName", ICON, new Intent()) .setPackageName(PACKAGE_NAME) - .setUid(0) + .setUserHandle(new UserHandle(0)) .build(); PeopleSpaceTile actual = PeopleSpaceUtils .augmentTileFromVisibleNotifications(mContext, tile, @@ -526,7 +526,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { new PeopleSpaceTile .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent()) .setPackageName(PACKAGE_NAME) - .setUid(0) + .setUserHandle(new UserHandle(0)) .build(); List<PeopleSpaceTile> actualList = PeopleSpaceUtils .augmentTilesFromVisibleNotifications( @@ -545,13 +545,13 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { new PeopleSpaceTile .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent()) .setPackageName(PACKAGE_NAME) - .setUid(1) + .setUserHandle(new UserHandle(0)) .build(); PeopleSpaceTile tile2 = new PeopleSpaceTile .Builder(SHORTCUT_ID_2, "userName2", ICON, new Intent()) .setPackageName(PACKAGE_NAME) - .setUid(0) + .setUserHandle(new UserHandle(0)) .build(); List<PeopleSpaceTile> actualList = PeopleSpaceUtils .augmentTilesFromVisibleNotifications(mContext, List.of(tile1, tile2), 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 1c8324c524f4..800d8593035d 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 @@ -109,7 +109,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { new PeopleSpaceTile .Builder(SHORTCUT_ID, "username", ICON, new Intent()) .setPackageName(TEST_PACKAGE_A) - .setUid(0) + .setUserHandle(new UserHandle(1)) .setNotificationKey(NOTIFICATION_KEY + "1") .setNotificationContent(NOTIFICATION_CONTENT) .setNotificationDataUri(URI) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java index 2dfd38832997..d40e6a4586b8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java @@ -89,6 +89,8 @@ public class QSFooterViewControllerTest extends LeakCheckedTest { private View mEdit; @Mock private MultiUserSwitch mMultiUserSwitch; + @Mock + private View mPowerMenuLiteView; private QSFooterViewController mController; @@ -111,11 +113,12 @@ public class QSFooterViewControllerTest extends LeakCheckedTest { when(mView.findViewById(R.id.build)).thenReturn(mBuildText); when(mView.findViewById(android.R.id.edit)).thenReturn(mEdit); when(mView.findViewById(R.id.multi_user_switch)).thenReturn(mMultiUserSwitch); + when(mView.findViewById(R.id.pm_lite)).thenReturn(mPowerMenuLiteView); mController = new QSFooterViewController(mView, mUserManager, mUserInfoController, mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController, new QSDetailDisplayer(), mQuickQSPanelController, mFakeTunerService, - mMetricsLogger); + mMetricsLogger, false); mController.init(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java index 0dfebab59feb..a2a179ea29df 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java @@ -121,7 +121,6 @@ public class QSPanelControllerTest extends SysuiTestCase { mQSTileHost, mQSCustomizerController, true, mMediaHost, mQSTileRevealControllerFactory, mDumpManager, mMetricsLogger, mUiEventLogger, mQSLogger, mBrightnessControllerFactory, mToggleSliderViewControllerFactory, - /* labelsFlag */ false, mFeatureFlags ); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt index 587020090433..cb380d51bd79 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt @@ -87,7 +87,6 @@ class QuickQSPanelControllerTest : SysuiTestCase() { uiEventLogger, qsLogger, dumpManager, - false, featureFlags ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java index a75c39c33f14..9e62a6263a43 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java @@ -24,6 +24,7 @@ import android.graphics.Paint; import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.RenderNode; +import android.os.ICancellationSignal; import android.os.RemoteException; import android.view.IScrollCaptureCallbacks; import android.view.IScrollCaptureConnection; @@ -46,7 +47,7 @@ class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub { } @Override - public void startCapture(Surface surface) { + public ICancellationSignal startCapture(Surface surface) { mSurface = surface; mHwuiContext = new HwuiContext(false, surface); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); @@ -56,27 +57,28 @@ class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub { } catch (RemoteException e) { e.rethrowAsRuntimeException(); } + return null; } @Override - public void requestImage(Rect rect) { + public ICancellationSignal requestImage(Rect rect) { Canvas canvas = mHwuiContext.lockCanvas(rect.width(), rect.height()); mPaint.setColor(mColors[mNextColor]); canvas.drawRect(rect, mPaint); mNextColor = (mNextColor++) % mColors.length; - long frameNumber = mSurface.getNextFrameNumber(); mHwuiContext.unlockAndPost(canvas); try { - mCallbacks.onCaptureBufferSent(frameNumber, rect); + mCallbacks.onImageRequestCompleted(0, rect); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } + return null; } @Override - public void endCapture() { + public ICancellationSignal endCapture() { try { - mCallbacks.onConnectionClosed(); + mCallbacks.onCaptureEnded(); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } finally { @@ -84,6 +86,12 @@ class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub { mSurface = null; mCallbacks = null; } + return null; + } + + @Override + public void close() throws RemoteException { + } // From android.view.Surface, but issues render requests synchronously with waitForPresent(true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java index 580f800fbc44..802b462ec10e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java @@ -29,7 +29,6 @@ import static org.mockito.Mockito.verify; import static java.util.Objects.requireNonNull; import android.content.Context; -import android.graphics.Point; import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.RemoteException; @@ -37,6 +36,7 @@ import android.testing.AndroidTestingRunner; import android.view.Display; import android.view.IScrollCaptureCallbacks; import android.view.IWindowManager; +import android.view.ScrollCaptureResponse; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; @@ -82,10 +82,11 @@ public class ScrollCaptureClientTest extends SysuiTestCase { public void testBasicClientFlow() throws RemoteException { doAnswer((Answer<Void>) invocation -> { IScrollCaptureCallbacks cb = invocation.getArgument(3); - cb.onConnected( - new FakeScrollCaptureConnection(cb), - /* scrollBounds */ new Rect(0, 0, 100, 100), - /* positionInWindow */ new Point(0, 0)); + cb.onScrollCaptureResponse(new ScrollCaptureResponse.Builder() + .setBoundsInWindow(new Rect(0, 0, 100, 100)) + .setWindowBounds(new Rect(0, 0, 100, 100)) + .setConnection(new FakeScrollCaptureConnection(cb)) + .build()); return null; }).when(mWm).requestScrollCapture(/* displayId */ anyInt(), /* token */ isNull(), /* taskId */ anyInt(), any(IScrollCaptureCallbacks.class)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java index 2b3ca7c00a49..6564d588f4ea 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java @@ -19,15 +19,14 @@ package com.android.systemui.screenshot; import static org.junit.Assert.fail; import android.content.Intent; -import android.graphics.Point; import android.graphics.Rect; import android.os.RemoteException; import android.testing.AndroidTestingRunner; import android.util.Log; import android.view.Display; import android.view.IScrollCaptureCallbacks; -import android.view.IScrollCaptureConnection; import android.view.IWindowManager; +import android.view.ScrollCaptureResponse; import android.view.WindowManagerGlobal; import androidx.test.filters.SmallTest; @@ -67,31 +66,27 @@ public class ScrollCaptureTest extends SysuiTestCase { wms.requestScrollCapture(Display.DEFAULT_DISPLAY, null, -1, new IScrollCaptureCallbacks.Stub() { @Override - public void onConnected( - IScrollCaptureConnection connection, Rect scrollBounds, - Point positionInWindow) { - Log.d(TAG, - "client connected: " + connection + "[scrollBounds= " - + scrollBounds + ", " - + "positionInWindow=" + positionInWindow + "]"); + public void onScrollCaptureResponse(ScrollCaptureResponse response) + throws RemoteException { + Log.d(TAG, "onScrollCaptureResponse: " + response); latch.countDown(); } @Override - public void onUnavailable() { - } - - @Override public void onCaptureStarted() { } @Override - public void onCaptureBufferSent(long frameNumber, Rect capturedArea) { + public void onImageRequestCompleted(int i, Rect rect) + throws RemoteException { + } @Override - public void onConnectionClosed() { + public void onCaptureEnded() throws RemoteException { + } + }); } catch (RemoteException e) { Log.e(TAG, "request failed", e); 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 e798207c6947..6067b42e0ef8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java @@ -32,7 +32,6 @@ import static com.android.systemui.theme.ThemeOverlayApplier.SYSUI_PACKAGE; import static com.android.systemui.theme.ThemeOverlayApplier.THEME_CATEGORIES; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; @@ -88,7 +87,9 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { private static final String THEMEPICKER_PACKAGE = "com.android.wallpaper"; private static final String LAUNCHER_PACKAGE = "com.android.launcher3"; private static final UserHandle TEST_USER = UserHandle.of(5); - private static final Set<UserHandle> TEST_USER_HANDLES = Sets.newHashSet(TEST_USER); + private static final UserHandle TEST_USER_MANAGED_PROFILE = UserHandle.of(6); + private static final Set<UserHandle> TEST_USER_HANDLES = + Sets.newHashSet(TEST_USER_MANAGED_PROFILE); @Mock OverlayManager mOverlayManager; @@ -159,13 +160,19 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { THEMEPICKER_PACKAGE, OVERLAY_CATEGORY_ICON_THEME_PICKER, false), createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_THEME_PICKER, THEMEPICKER_PACKAGE, OVERLAY_CATEGORY_ICON_THEME_PICKER, true))); + + OverlayInfo launcherTargetInfo = new OverlayInfo("packageName", LAUNCHER_PACKAGE, + null, null, "/", 0, 0, 0, false); + when(mOverlayManager.getOverlayInfo(any(OverlayIdentifier.class), any())) + .thenReturn(launcherTargetInfo); clearInvocations(mOverlayManager); verify(mDumpManager).registerDumpable(any(), any()); } @Test public void allCategoriesSpecified_allEnabledExclusively() { - mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER_HANDLES); + mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(), + TEST_USER_HANDLES); verify(mOverlayManager).commit(any()); for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) { @@ -176,7 +183,8 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { @Test public void allCategoriesSpecified_sysuiCategoriesAlsoAppliedToSysuiUser() { - mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER_HANDLES); + mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(), + TEST_USER_HANDLES); for (Map.Entry<String, OverlayIdentifier> entry : ALL_CATEGORIES_MAP.entrySet()) { if (SYSTEM_USER_CATEGORIES.contains(entry.getKey())) { @@ -192,27 +200,25 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { @Test public void allCategoriesSpecified_enabledForAllUserHandles() { Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES); - UserHandle newUserHandle = UserHandle.of(10); - userHandles.add(newUserHandle); - mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, userHandles); + mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(), + userHandles); for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) { verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true), eq(TEST_USER.getIdentifier())); - verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true), - eq(newUserHandle.getIdentifier())); + // Not enabled for work profile because the target package is LAUNCHER_PACKAGE + verify(mTransactionBuilder, never()).setEnabled(eq(overlayPackage), eq(true), + eq(TEST_USER_MANAGED_PROFILE.getIdentifier())); } } @Test public void applyCurrentUserOverlays_createsPendingOverlays() { - Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES); - UserHandle newUserHandle = UserHandle.of(10); - userHandles.add(newUserHandle); - FabricatedOverlay[] pendingCreation = new FabricatedOverlay[] { + FabricatedOverlay[] pendingCreation = new FabricatedOverlay[]{ mock(FabricatedOverlay.class) }; - mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, pendingCreation, userHandles); + mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, pendingCreation, + TEST_USER.getIdentifier(), TEST_USER_HANDLES); for (FabricatedOverlay overlay : pendingCreation) { verify(mTransactionBuilder).registerFabricatedOverlay(eq(overlay)); @@ -220,20 +226,13 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { } @Test - public void allCategoriesSpecified_overlayManagerNotQueried() { - mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER_HANDLES); - - verify(mOverlayManager, never()) - .getOverlayInfosForTarget(anyString(), any(UserHandle.class)); - } - - @Test public void someCategoriesSpecified_specifiedEnabled_unspecifiedDisabled() { Map<String, OverlayIdentifier> categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP); categoryToPackage.remove(OVERLAY_CATEGORY_ICON_SETTINGS); categoryToPackage.remove(OVERLAY_CATEGORY_ICON_ANDROID); - mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER_HANDLES); + mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(), + TEST_USER_HANDLES); for (OverlayIdentifier overlayPackage : categoryToPackage.values()) { verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true), @@ -249,7 +248,8 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { @Test public void zeroCategoriesSpecified_allDisabled() { - mManager.applyCurrentUserOverlays(Maps.newArrayMap(), null, TEST_USER_HANDLES); + mManager.applyCurrentUserOverlays(Maps.newArrayMap(), null, TEST_USER.getIdentifier(), + TEST_USER_HANDLES); for (String category : THEME_CATEGORIES) { verify(mTransactionBuilder).setEnabled( @@ -263,7 +263,8 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { Map<String, OverlayIdentifier> categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP); categoryToPackage.put("blah.category", new OverlayIdentifier("com.example.blah.category")); - mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER_HANDLES); + mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(), + TEST_USER_HANDLES); verify(mTransactionBuilder, never()).setEnabled( eq(new OverlayIdentifier("com.example.blah.category")), eq(false), @@ -273,23 +274,6 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { eq(TEST_USER.getIdentifier())); } - @Test - public void overlayManagerOnlyQueriedForUnspecifiedPackages() { - Map<String, OverlayIdentifier> categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP); - categoryToPackage.remove(OVERLAY_CATEGORY_ICON_SETTINGS); - - mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER_HANDLES); - - verify(mOverlayManager).getOverlayInfosForTarget(SETTINGS_PACKAGE, UserHandle.SYSTEM); - verify(mOverlayManager, never()).getOverlayInfosForTarget(ANDROID_PACKAGE, - UserHandle.SYSTEM); - verify(mOverlayManager, never()).getOverlayInfosForTarget(SYSUI_PACKAGE, UserHandle.SYSTEM); - verify(mOverlayManager, never()).getOverlayInfosForTarget(LAUNCHER_PACKAGE, - UserHandle.SYSTEM); - verify(mOverlayManager, never()).getOverlayInfosForTarget(THEMEPICKER_PACKAGE, - UserHandle.SYSTEM); - } - private static OverlayInfo createOverlayInfo(String packageName, String targetPackageName, String category, boolean enabled) { return new OverlayInfo(packageName, null, targetPackageName, null, category, "", diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java index aa385effa931..d80c40fcd07b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java @@ -143,7 +143,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { ArgumentCaptor.forClass(Map.class); verify(mThemeOverlayApplier) - .applyCurrentUserOverlays(themeOverlays.capture(), any(), any()); + .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any()); // Assert that we received the colors that we were expecting assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE)) @@ -175,7 +175,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { ArgumentCaptor.forClass(Map.class); verify(mThemeOverlayApplier) - .applyCurrentUserOverlays(themeOverlays.capture(), any(), any()); + .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any()); // Assert that we received the colors that we were expecting assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE)) @@ -198,7 +198,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { ArgumentCaptor.forClass(Map.class); verify(mThemeOverlayApplier) - .applyCurrentUserOverlays(themeOverlays.capture(), any(), any()); + .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any()); // Assert that we received the colors that we were expecting assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE)) diff --git a/services/api/Android.bp b/services/api/Android.bp new file mode 100644 index 000000000000..b8ca5488c5cd --- /dev/null +++ b/services/api/Android.bp @@ -0,0 +1,29 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_visibility: ["//visibility:private"], +} + +filegroup { + name: "non-updatable-system-server-current.txt", + srcs: ["non-updatable-current.txt"], + visibility: ["//frameworks/base/api"], +} + +filegroup { + name: "non-updatable-system-server-removed.txt", + srcs: ["non-updatable-removed.txt"], + visibility: ["//frameworks/base/api"], +}
\ No newline at end of file diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 7518c7a8bdc9..50ad6617b1fe 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -137,7 +137,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -181,9 +180,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } switch (action) { - case Intent.ACTION_CONFIGURATION_CHANGED: - onConfigurationChanged(); - break; case Intent.ACTION_MANAGED_PROFILE_AVAILABLE: case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE: synchronized (mLock) { @@ -243,8 +239,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku private Handler mSaveStateHandler; private Handler mCallbackHandler; - private Locale mLocale; - private final SparseIntArray mNextAppWidgetIds = new SparseIntArray(); private boolean mSafeMode; @@ -290,13 +284,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } private void registerBroadcastReceiver() { - // Register for configuration changes so we can update the names - // of the widgets when the locale changes. - IntentFilter configFilter = new IntentFilter(); - configFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); - mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, - configFilter, null, null); - // Register for broadcasts about package install, etc., so we can // update the provider list. IntentFilter packageFilter = new IntentFilter(); @@ -338,62 +325,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku mSafeMode = safeMode; } - private void onConfigurationChanged() { - if (DEBUG) { - Slog.i(TAG, "onConfigurationChanged()"); - } - - Locale revised = Locale.getDefault(); - if (revised == null || mLocale == null || !revised.equals(mLocale)) { - mLocale = revised; - - synchronized (mLock) { - SparseIntArray changedGroups = null; - - // Note: updateProvidersForPackageLocked() may remove providers, so we must copy the - // list of installed providers and skip providers that we don't need to update. - // Also note that remove the provider does not clear the Provider component data. - ArrayList<Provider> installedProviders = new ArrayList<>(mProviders); - HashSet<ProviderId> removedProviders = new HashSet<>(); - - int N = installedProviders.size(); - for (int i = N - 1; i >= 0; i--) { - Provider provider = installedProviders.get(i); - - final int userId = provider.getUserId(); - if (!mUserManager.isUserUnlockingOrUnlocked(userId) || - isProfileWithLockedParent(userId)) { - continue; - } - ensureGroupStateLoadedLocked(userId); - - if (!removedProviders.contains(provider.id)) { - final boolean changed = updateProvidersForPackageLocked( - provider.id.componentName.getPackageName(), - provider.getUserId(), removedProviders); - - if (changed) { - if (changedGroups == null) { - changedGroups = new SparseIntArray(); - } - final int groupId = mSecurityPolicy.getGroupParent( - provider.getUserId()); - changedGroups.put(groupId, groupId); - } - } - } - - if (changedGroups != null) { - final int groupCount = changedGroups.size(); - for (int i = 0; i < groupCount; i++) { - final int groupId = changedGroups.get(i); - saveGroupStateAsync(groupId); - } - } - } - } - } - private void onPackageBroadcastReceived(Intent intent, int userId) { final String action = intent.getAction(); boolean added = false; 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 01055beea4f8..21cae453d702 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; @@ -747,6 +742,15 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } } } + + if (association.isNotifyOnDeviceNearby()) { + ServiceConnector<ICompanionDeviceService> serviceConnector = + mDeviceListenerServiceConnectors.forUser(association.getUserId()) + .get(association.getPackageName()); + if (serviceConnector != null) { + serviceConnector.unbind(); + } + } } private void updateSpecialAccessPermissionForAssociatedPackage(Association association) { diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java index 84e429dc83e4..f2782f64995a 100644 --- a/services/core/java/com/android/server/SensorPrivacyService.java +++ b/services/core/java/com/android/server/SensorPrivacyService.java @@ -25,9 +25,9 @@ import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.content.Intent.EXTRA_PACKAGE_NAME; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.hardware.SensorPrivacyManager.EXTRA_SENSOR; +import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; +import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE; import static android.os.UserHandle.USER_SYSTEM; -import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA; -import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE; import static android.service.SensorPrivacyIndividualEnabledSensorProto.UNKNOWN; import android.annotation.NonNull; @@ -406,12 +406,12 @@ public final class SensorPrivacyService extends SystemService { */ @Override public void setSensorPrivacy(boolean enable) { + enforceManageSensorPrivacyPermission(); // Keep the state consistent between all users to make it a single global state forAllUsers(userId -> setSensorPrivacy(userId, enable)); } private void setSensorPrivacy(@UserIdInt int userId, boolean enable) { - enforceSensorPrivacyPermission(); synchronized (mLock) { mEnabled.put(userId, enable); persistSensorPrivacyStateLocked(); @@ -421,7 +421,7 @@ public final class SensorPrivacyService extends SystemService { @Override public void setIndividualSensorPrivacy(@UserIdInt int userId, int sensor, boolean enable) { - enforceSensorPrivacyPermission(); + enforceManageSensorPrivacyPermission(); synchronized (mLock) { SparseBooleanArray userIndividualEnabled = mIndividualEnabled.get(userId, new SparseBooleanArray()); @@ -448,6 +448,7 @@ public final class SensorPrivacyService extends SystemService { @Override public void setIndividualSensorPrivacyForProfileGroup(@UserIdInt int userId, int sensor, boolean enable) { + enforceManageSensorPrivacyPermission(); int parentId = mUserManagerInternal.getProfileParentId(userId); forAllUsers(userId2 -> { if (parentId == mUserManagerInternal.getProfileParentId(userId2)) { @@ -460,21 +461,35 @@ public final class SensorPrivacyService extends SystemService { * Enforces the caller contains the necessary permission to change the state of sensor * privacy. */ - private void enforceSensorPrivacyPermission() { - if (mContext.checkCallingOrSelfPermission( - MANAGE_SENSOR_PRIVACY) == PERMISSION_GRANTED) { - return; - } - throw new SecurityException( + private void enforceManageSensorPrivacyPermission() { + enforcePermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY, "Changing sensor privacy requires the following permission: " + MANAGE_SENSOR_PRIVACY); } /** + * Enforces the caller contains the necessary permission to observe changes to the sate of + * sensor privacy. + */ + private void enforceObserveSensorPrivacyPermission() { + enforcePermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY, + "Observing sensor privacy changes requires the following permission: " + + android.Manifest.permission.OBSERVE_SENSOR_PRIVACY); + } + + private void enforcePermission(String permission, String message) { + if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) { + return; + } + throw new SecurityException(message); + } + + /** * Returns whether sensor privacy is enabled. */ @Override public boolean isSensorPrivacyEnabled() { + enforceObserveSensorPrivacyPermission(); return isSensorPrivacyEnabled(USER_SYSTEM); } @@ -486,6 +501,7 @@ public final class SensorPrivacyService extends SystemService { @Override public boolean isIndividualSensorPrivacyEnabled(@UserIdInt int userId, int sensor) { + enforceObserveSensorPrivacyPermission(); synchronized (mLock) { SparseBooleanArray states = mIndividualEnabled.get(userId); if (states == null) { @@ -703,6 +719,7 @@ public final class SensorPrivacyService extends SystemService { */ @Override public void addSensorPrivacyListener(ISensorPrivacyListener listener) { + enforceObserveSensorPrivacyPermission(); if (listener == null) { throw new NullPointerException("listener cannot be null"); } @@ -715,6 +732,7 @@ public final class SensorPrivacyService extends SystemService { @Override public void addIndividualSensorPrivacyListener(int userId, int sensor, ISensorPrivacyListener listener) { + enforceObserveSensorPrivacyPermission(); if (listener == null) { throw new NullPointerException("listener cannot be null"); } @@ -726,6 +744,7 @@ public final class SensorPrivacyService extends SystemService { */ @Override public void removeSensorPrivacyListener(ISensorPrivacyListener listener) { + enforceObserveSensorPrivacyPermission(); if (listener == null) { throw new NullPointerException("listener cannot be null"); } @@ -735,6 +754,7 @@ public final class SensorPrivacyService extends SystemService { @Override public void suppressIndividualSensorPrivacyReminders(int userId, String packageName, IBinder token, boolean suppress) { + enforceManageSensorPrivacyPermission(); Objects.requireNonNull(packageName); Objects.requireNonNull(token); @@ -886,13 +906,13 @@ public final class SensorPrivacyService extends SystemService { } /** - * Convert a string into a {@link SensorPrivacyManager.IndividualSensor id}. + * Convert a string into a {@link SensorPrivacyManager.Sensors.Sensor id}. * * @param sensor The name to convert * * @return The id corresponding to the name */ - private @SensorPrivacyManager.IndividualSensor int sensorStrToId(@Nullable String sensor) { + private @SensorPrivacyManager.Sensors.Sensor int sensorStrToId(@Nullable String sensor) { if (sensor == null) { return UNKNOWN; } @@ -950,7 +970,7 @@ public final class SensorPrivacyService extends SystemService { return -1; } - enforceSensorPrivacyPermission(); + enforceManageSensorPrivacyPermission(); synchronized (mLock) { SparseBooleanArray individualEnabled = diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java index c2d8fa24157a..8a2894c84cc4 100644 --- a/services/core/java/com/android/server/ServiceWatcher.java +++ b/services/core/java/com/android/server/ServiceWatcher.java @@ -45,14 +45,18 @@ import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; +import android.util.ArrayMap; import android.util.Log; +import com.android.internal.annotations.Immutable; import com.android.internal.content.PackageMonitor; import com.android.internal.util.Preconditions; +import com.android.internal.util.function.pooled.PooledLambda; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.function.Predicate; @@ -87,26 +91,30 @@ public class ServiceWatcher implements ServiceConnection { /** Function to run on binder interface when first bound. */ public interface OnBindRunner { /** Called to run client code with the binder. */ - void run(IBinder binder, ComponentName service) throws RemoteException; + void run(IBinder binder, BoundService service) throws RemoteException; } /** * Information on the service ServiceWatcher has selected as the best option for binding. */ - private static final class ServiceInfo implements Comparable<ServiceInfo> { + @Immutable + public static final class BoundService implements Comparable<BoundService> { - public static final ServiceInfo NONE = new ServiceInfo(Integer.MIN_VALUE, null, - UserHandle.USER_NULL, false); + public static final BoundService NONE = new BoundService(Integer.MIN_VALUE, null, + false, null, -1); public final int version; - @Nullable public final ComponentName component; - @UserIdInt public final int userId; + @Nullable + public final ComponentName component; public final boolean serviceIsMultiuser; + public final int uid; + @Nullable + public final Bundle metadata; - ServiceInfo(ResolveInfo resolveInfo, int currentUserId) { + BoundService(ResolveInfo resolveInfo) { Preconditions.checkArgument(resolveInfo.serviceInfo.getComponentName() != null); - Bundle metadata = resolveInfo.serviceInfo.metaData; + metadata = resolveInfo.serviceInfo.metaData; if (metadata != null) { version = metadata.getInt(EXTRA_SERVICE_VERSION, Integer.MIN_VALUE); serviceIsMultiuser = metadata.getBoolean(EXTRA_SERVICE_IS_MULTIUSER, false); @@ -116,16 +124,17 @@ public class ServiceWatcher implements ServiceConnection { } component = resolveInfo.serviceInfo.getComponentName(); - userId = serviceIsMultiuser ? UserHandle.USER_SYSTEM : currentUserId; + uid = resolveInfo.serviceInfo.applicationInfo.uid; } - private ServiceInfo(int version, @Nullable ComponentName component, int userId, - boolean serviceIsMultiuser) { + private BoundService(int version, @Nullable ComponentName component, + boolean serviceIsMultiuser, @Nullable Bundle metadata, int uid) { Preconditions.checkArgument(component != null || version == Integer.MIN_VALUE); this.version = version; this.component = component; - this.userId = userId; this.serviceIsMultiuser = serviceIsMultiuser; + this.metadata = metadata; + this.uid = uid; } public @Nullable String getPackageName() { @@ -137,21 +146,21 @@ public class ServiceWatcher implements ServiceConnection { if (this == o) { return true; } - if (!(o instanceof ServiceInfo)) { + if (!(o instanceof BoundService)) { return false; } - ServiceInfo that = (ServiceInfo) o; - return version == that.version && userId == that.userId + BoundService that = (BoundService) o; + return version == that.version && uid == that.uid && Objects.equals(component, that.component); } @Override public int hashCode() { - return Objects.hash(version, component, userId); + return Objects.hash(version, component, uid); } @Override - public int compareTo(ServiceInfo that) { + public int compareTo(BoundService that) { // ServiceInfos with higher version numbers always win (having a version number > // MIN_VALUE implies having a non-null component). if version numbers are equal, a // non-null component wins over a null component. if the version numbers are equal and @@ -164,10 +173,11 @@ public class ServiceWatcher implements ServiceConnection { } else if (component != null && that.component == null) { ret = 1; } else { - if (userId != UserHandle.USER_SYSTEM && that.userId == UserHandle.USER_SYSTEM) { + if (UserHandle.getUserId(uid) != UserHandle.USER_SYSTEM + && UserHandle.getUserId(that.uid) == UserHandle.USER_SYSTEM) { ret = -1; - } else if (userId == UserHandle.USER_SYSTEM - && that.userId != UserHandle.USER_SYSTEM) { + } else if (UserHandle.getUserId(uid) == UserHandle.USER_SYSTEM + && UserHandle.getUserId(that.uid) != UserHandle.USER_SYSTEM) { ret = 1; } } @@ -180,7 +190,8 @@ public class ServiceWatcher implements ServiceConnection { if (component == null) { return "none"; } else { - return component.toShortString() + "@" + version + "[u" + userId + "]"; + return component.toShortString() + "@" + version + "[u" + + UserHandle.getUserId(uid) + "]"; } } } @@ -227,17 +238,23 @@ public class ServiceWatcher implements ServiceConnection { } }; - @Nullable private final OnBindRunner mOnBind; - @Nullable private final Runnable mOnUnbind; + // read/write from handler thread only + private final Map<ComponentName, BoundService> mPendingBinds = new ArrayMap<>(); + + @Nullable + private final OnBindRunner mOnBind; + + @Nullable + private final Runnable mOnUnbind; - // write from caller thread only, read anywhere - private volatile boolean mRegistered; + // read/write from handler thread only + private boolean mRegistered; // read/write from handler thread only private int mCurrentUserId; // write from handler thread only, read anywhere - private volatile ServiceInfo mTargetService; + private volatile BoundService mTargetService; private volatile IBinder mBinder; public ServiceWatcher(Context context, String action, @@ -274,7 +291,7 @@ public class ServiceWatcher implements ServiceConnection { mCurrentUserId = UserHandle.USER_NULL; - mTargetService = ServiceInfo.NONE; + mTargetService = BoundService.NONE; mBinder = null; } @@ -299,6 +316,11 @@ public class ServiceWatcher implements ServiceConnection { * Starts the process of determining the best matching service and maintaining a binding to it. */ public void register() { + mHandler.sendMessage(PooledLambda.obtainMessage(ServiceWatcher::registerInternal, + ServiceWatcher.this)); + } + + private void registerInternal() { Preconditions.checkState(!mRegistered); mPackageMonitor.register(mContext, UserHandle.ALL, true, mHandler); @@ -309,6 +331,8 @@ public class ServiceWatcher implements ServiceConnection { mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, intentFilter, null, mHandler); + // TODO: This makes the behavior of the class unpredictable as the caller needs + // to know the internal impl detail that calling register would pick the current user. mCurrentUserId = ActivityManager.getCurrentUser(); mRegistered = true; @@ -320,6 +344,11 @@ public class ServiceWatcher implements ServiceConnection { * Stops the process of determining the best matching service and releases any binding. */ public void unregister() { + mHandler.sendMessage(PooledLambda.obtainMessage(ServiceWatcher::unregisterInternal, + ServiceWatcher.this)); + } + + private void unregisterInternal() { Preconditions.checkState(mRegistered); mRegistered = false; @@ -333,7 +362,7 @@ public class ServiceWatcher implements ServiceConnection { private void onBestServiceChanged(boolean forceRebind) { Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); - ServiceInfo bestServiceInfo = ServiceInfo.NONE; + BoundService bestServiceInfo = BoundService.NONE; if (mRegistered) { List<ResolveInfo> resolveInfos = mContext.getPackageManager().queryIntentServicesAsUser( @@ -344,7 +373,7 @@ public class ServiceWatcher implements ServiceConnection { if (!mServiceCheckPredicate.test(resolveInfo)) { continue; } - ServiceInfo serviceInfo = new ServiceInfo(resolveInfo, mCurrentUserId); + BoundService serviceInfo = new BoundService(resolveInfo); if (serviceInfo.compareTo(bestServiceInfo) > 0) { bestServiceInfo = serviceInfo; } @@ -356,21 +385,22 @@ public class ServiceWatcher implements ServiceConnection { } } - private void rebind(ServiceInfo newServiceInfo) { + private void rebind(BoundService newServiceInfo) { Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); - if (!mTargetService.equals(ServiceInfo.NONE)) { + if (!mTargetService.equals(BoundService.NONE)) { if (D) { Log.d(TAG, "[" + mIntent.getAction() + "] unbinding from " + mTargetService); } mContext.unbindService(this); onServiceDisconnected(mTargetService.component); - mTargetService = ServiceInfo.NONE; + mPendingBinds.remove(mTargetService.component); + mTargetService = BoundService.NONE; } mTargetService = newServiceInfo; - if (mTargetService.equals(ServiceInfo.NONE)) { + if (mTargetService.equals(BoundService.NONE)) { return; } @@ -381,10 +411,12 @@ public class ServiceWatcher implements ServiceConnection { Intent bindIntent = new Intent(mIntent).setComponent(mTargetService.component); if (!mContext.bindServiceAsUser(bindIntent, this, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND | BIND_NOT_VISIBLE, - mHandler, UserHandle.of(mTargetService.userId))) { - mTargetService = ServiceInfo.NONE; + mHandler, UserHandle.of(UserHandle.getUserId(mTargetService.uid)))) { + mTargetService = BoundService.NONE; Log.e(TAG, getLogPrefix() + " unexpected bind failure - retrying later"); mHandler.postDelayed(() -> onBestServiceChanged(false), RETRY_DELAY_MS); + } else { + mPendingBinds.put(mTargetService.component, mTargetService); } } @@ -397,10 +429,15 @@ public class ServiceWatcher implements ServiceConnection { Log.d(TAG, getLogPrefix() + " connected to " + component.toShortString()); } + final BoundService boundService = mPendingBinds.remove(component); + if (boundService == null) { + return; + } + mBinder = binder; if (mOnBind != null) { try { - mOnBind.run(binder, component); + mOnBind.run(binder, boundService); } catch (RuntimeException | RemoteException e) { // binders may propagate some specific non-RemoteExceptions from the other side // through the binder as well - we cannot allow those to crash the system server diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 2f9819997257..6be7f05f6cc6 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -51,6 +51,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerInternal; +import android.app.AnrController; import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.KeyguardManager; @@ -938,14 +939,29 @@ class StorageManagerService extends IStorageManager.Stub if (transcodeEnabled) { LocalServices.getService(ActivityManagerInternal.class) - .registerAnrController((packageName, uid) -> { - try { - return mStorageSessionController.getAnrDelayMillis(packageName, uid); - } catch (ExternalStorageServiceException e) { - Log.e(TAG, "Failed to get ANR delay for " + packageName, e); - return 0; - } - }); + .registerAnrController(new ExternalStorageServiceAnrController()); + } + } + + // TODO(b/170486601): Check transcoding status based on events pushed from the MediaProvider + private class ExternalStorageServiceAnrController implements AnrController { + @Override + public long getAnrDelayMillis(String packageName, int uid) { + int delay = SystemProperties.getInt("sys.fuse.transcode_anr_delay", 0); + Log.d(TAG, "getAnrDelayMillis: " + packageName + ". Delaying for " + delay + "ms"); + return delay; + } + + @Override + public void onAnrDelayStarted(String packageName, int uid) { + Log.d(TAG, "onAnrDelayStarted: " + packageName); + } + + @Override + public boolean onAnrDelayCompleted(String packageName, int uid) { + boolean show = SystemProperties.getBoolean("sys.fuse.transcode_anr_dialog_show", true); + Log.d(TAG, "onAnrDelayCompleted: " + packageName + ". Show: " + show); + return show; } } diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING index 95af84293377..a09dbc7e599d 100644 --- a/services/core/java/com/android/server/TEST_MAPPING +++ b/services/core/java/com/android/server/TEST_MAPPING @@ -21,7 +21,9 @@ } ], "file_patterns": ["NotificationManagerService\\.java"] - }, + } + ], + "presubmit-large": [ { "name": "CtsScopedStorageCoreHostTest", "file_patterns": ["StorageManagerService\\.java"] diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index b09b6ca61377..5a5f1a3f3723 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -313,9 +313,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private List<PhysicalChannelConfig> mPhysicalChannelConfigs; - private boolean mIsDataEnabled = false; + private boolean[] mIsDataEnabled; - private int mDataEnabledReason; + private int[] mDataEnabledReason; private Map<Integer, Long> mAllowedNetworkTypesList; @@ -524,6 +524,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mOutgoingCallEmergencyNumber = copyOf(mOutgoingCallEmergencyNumber, mNumPhones); mOutgoingSmsEmergencyNumber = copyOf(mOutgoingSmsEmergencyNumber, mNumPhones); mTelephonyDisplayInfos = copyOf(mTelephonyDisplayInfos, mNumPhones); + mIsDataEnabled= copyOf(mIsDataEnabled, mNumPhones); + mDataEnabledReason = copyOf(mDataEnabledReason, mNumPhones); // ds -> ss switch. if (mNumPhones < oldNumPhones) { @@ -565,6 +567,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mPreciseDataConnectionStates.add(new ArrayMap<>()); mBarringInfo.add(i, new BarringInfo()); mTelephonyDisplayInfos[i] = null; + mIsDataEnabled[i] = false; + mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER; mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build()); } } @@ -626,6 +630,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mTelephonyDisplayInfos = new TelephonyDisplayInfo[numPhones]; mPhysicalChannelConfigs = new ArrayList<>(); mAllowedNetworkTypesList = new HashMap<>(); + mIsDataEnabled = new boolean[numPhones]; + mDataEnabledReason = new int[numPhones]; for (int i = 0; i < numPhones; i++) { mCallState[i] = TelephonyManager.CALL_STATE_IDLE; mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE; @@ -655,6 +661,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mPreciseDataConnectionStates.add(new ArrayMap<>()); mBarringInfo.add(i, new BarringInfo()); mTelephonyDisplayInfos[i] = null; + mIsDataEnabled[i] = false; + mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER; mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build()); } @@ -1150,7 +1158,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (events.contains( PhoneStateListener.EVENT_DATA_ENABLED_CHANGED)) { try { - r.callback.onDataEnabledChanged(mIsDataEnabled, mDataEnabledReason); + r.callback.onDataEnabledChanged( + mIsDataEnabled[phoneId], mDataEnabledReason[phoneId]); } catch (RemoteException ex) { remove(r.binder); } @@ -2370,30 +2379,36 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { /** * Notify that the data enabled has changed. * + * @param phoneId the phone id. + * @param subId the subId. * @param enabled True if data is enabled, otherwise disabled. * @param reason Reason for data enabled/disabled. See {@code DATA_*} in * {@link TelephonyManager}. */ - public void notifyDataEnabled(boolean enabled, + public void notifyDataEnabled(int phoneId, int subId, boolean enabled, @TelephonyManager.DataEnabledReason int reason) { if (!checkNotifyPermission("notifyDataEnabled()")) { return; } if (VDBG) { - log("notifyDataEnabled: enabled=" + enabled + " reason=" + reason); + log("notifyDataEnabled: PhoneId=" + phoneId + " subId=" + subId + + " enabled=" + enabled + " reason=" + reason); } - mIsDataEnabled = enabled; - mDataEnabledReason = reason; synchronized (mRecords) { - for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_DATA_ENABLED_CHANGED)) { - try { - r.callback.onDataEnabledChanged(enabled, reason); - } catch (RemoteException ex) { - mRemoveList.add(r.binder); + if (validatePhoneId(phoneId)) { + mIsDataEnabled[phoneId] = enabled; + mDataEnabledReason[phoneId] = reason; + for (Record r : mRecords) { + if (r.matchPhoneStateListenerEvent( + PhoneStateListener.EVENT_DATA_ENABLED_CHANGED) + && idMatch(r.subId, subId, phoneId)) { + try { + r.callback.onDataEnabledChanged(enabled, reason); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } } } } @@ -2481,6 +2496,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println("mOutgoingSmsEmergencyNumber=" + mOutgoingSmsEmergencyNumber[i]); pw.println("mBarringInfo=" + mBarringInfo.get(i)); pw.println("mTelephonyDisplayInfo=" + mTelephonyDisplayInfos[i]); + pw.println("mIsDataEnabled=" + mIsDataEnabled); + pw.println("mDataEnabledReason=" + mDataEnabledReason); pw.decreaseIndent(); } pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState); @@ -2491,8 +2508,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println("mDefaultPhoneId=" + mDefaultPhoneId); pw.println("mDefaultSubId=" + mDefaultSubId); pw.println("mPhysicalChannelConfigs=" + mPhysicalChannelConfigs); - pw.println("mIsDataEnabled=" + mIsDataEnabled); - pw.println("mDataEnabledReason=" + mDataEnabledReason); pw.decreaseIndent(); diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 2efc83c4af06..e5ef9353135d 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -20,10 +20,35 @@ import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND; import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND; import static android.Manifest.permission.SYSTEM_ALERT_WINDOW; import static android.app.ActivityManager.PROCESS_STATE_HEAVY_WEIGHT; +import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT; import static android.app.ActivityManager.PROCESS_STATE_RECEIVER; import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST; +import static android.os.PowerWhitelistManager.REASON_ACTIVITY_STARTER; +import static android.os.PowerWhitelistManager.REASON_ALLOWLISTED_PACKAGE; +import static android.os.PowerWhitelistManager.REASON_BACKGROUND_ACTIVITY_PERMISSION; +import static android.os.PowerWhitelistManager.REASON_BACKGROUND_FGS_PERMISSION; +import static android.os.PowerWhitelistManager.REASON_COMPANION_DEVICE_MANAGER; +import static android.os.PowerWhitelistManager.REASON_DENIED; +import static android.os.PowerWhitelistManager.REASON_DEVICE_DEMO_MODE; +import static android.os.PowerWhitelistManager.REASON_DEVICE_OWNER; +import static android.os.PowerWhitelistManager.REASON_EXEMPTED_PACKAGE; +import static android.os.PowerWhitelistManager.REASON_FGS_BINDING; +import static android.os.PowerWhitelistManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION; +import static android.os.PowerWhitelistManager.REASON_INSTR_BACKGROUND_FGS_PERMISSION; +import static android.os.PowerWhitelistManager.REASON_PROC_STATE_PERSISTENT; +import static android.os.PowerWhitelistManager.REASON_PROC_STATE_PERSISTENT_UI; +import static android.os.PowerWhitelistManager.REASON_PROC_STATE_TOP; +import static android.os.PowerWhitelistManager.REASON_PROFILE_OWNER; +import static android.os.PowerWhitelistManager.REASON_START_ACTIVITY_FLAG; +import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALERT_WINDOW_PERMISSION; +import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALLOW_LISTED; +import static android.os.PowerWhitelistManager.REASON_SYSTEM_UID; +import static android.os.PowerWhitelistManager.REASON_UID_VISIBLE; +import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED; +import static android.os.PowerWhitelistManager.getReasonCodeFromProcState; +import static android.os.PowerWhitelistManager.reasonCodeToString; import static android.os.Process.NFC_UID; import static android.os.Process.ROOT_UID; import static android.os.Process.SHELL_UID; @@ -43,7 +68,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE_E import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; @@ -86,6 +110,8 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.PowerWhitelistManager; +import android.os.PowerWhitelistManager.ReasonCode; import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteException; @@ -130,8 +156,6 @@ import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Comparator; @@ -152,58 +176,6 @@ public final class ActiveServices { private static final boolean SHOW_DUNGEON_NOTIFICATION = false; - public static final int FGS_FEATURE_DENIED = 0; - public static final int FGS_FEATURE_ALLOWED_BY_UID_STATE = 1; - public static final int FGS_FEATURE_ALLOWED_BY_PROC_STATE = 2; - public static final int FGS_FEATURE_ALLOWED_BY_UID_VISIBLE = 3; - public static final int FGS_FEATURE_ALLOWED_BY_FLAG = 4; - public static final int FGS_FEATURE_ALLOWED_BY_SYSTEM_UID = 5; - public static final int FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION = 6; - public static final int FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION = 7; - public static final int FGS_FEATURE_ALLOWED_BY_ACTIVITY_TOKEN = 8; - public static final int FGS_FEATURE_ALLOWED_BY_FGS_TOKEN = 9; - public static final int FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION = 10; - public static final int FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION = 12; - public static final int FGS_FEATURE_ALLOWED_BY_ALLOWLIST = 13; - public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER = 14; - public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST = 15; - public static final int FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION = 16; - public static final int FGS_FEATURE_ALLOWED_BY_FGS_BINDING = 17; - public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE = 18; - public static final int FGS_FEATURE_ALLOWED_BY_PROCESS_RECORD = 19; - public static final int FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES = 20; - public static final int FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER = 21; - public static final int FGS_FEATURE_ALLOWED_BY_COMPANION_APP = 22; - public static final int FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER = 23; - - @IntDef(flag = true, prefix = { "FGS_FEATURE_" }, value = { - FGS_FEATURE_DENIED, - FGS_FEATURE_ALLOWED_BY_UID_STATE, - FGS_FEATURE_ALLOWED_BY_PROC_STATE, - FGS_FEATURE_ALLOWED_BY_UID_VISIBLE, - FGS_FEATURE_ALLOWED_BY_FLAG, - FGS_FEATURE_ALLOWED_BY_SYSTEM_UID, - FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION, - FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION, - FGS_FEATURE_ALLOWED_BY_ACTIVITY_TOKEN, - FGS_FEATURE_ALLOWED_BY_FGS_TOKEN, - FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION, - FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION, - FGS_FEATURE_ALLOWED_BY_ALLOWLIST, - FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER, - FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST, - FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION, - FGS_FEATURE_ALLOWED_BY_FGS_BINDING, - FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE, - FGS_FEATURE_ALLOWED_BY_PROCESS_RECORD, - FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES, - FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER, - FGS_FEATURE_ALLOWED_BY_COMPANION_APP, - FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER - }) - @Retention(RetentionPolicy.SOURCE) - public @interface FgsFeatureRetCode {} - // How long we wait for a service to finish executing. static final int SERVICE_TIMEOUT = 20*1000; @@ -275,7 +247,7 @@ public final class ActiveServices { AppWidgetManagerInternal mAppWidgetManagerInternal; - // white listed packageName. + // allowlisted packageName. ArraySet<String> mAllowListWhileInUsePermissionInFgs = new ArraySet<>(); // TODO: remove this after feature development is done @@ -675,7 +647,7 @@ public final class ActiveServices { if (fgRequired) { logFgsBackgroundStart(r); - if (r.mAllowStartForeground == FGS_FEATURE_DENIED && isBgFgsRestrictionEnabled(r)) { + if (r.mAllowStartForeground == REASON_DENIED && isBgFgsRestrictionEnabled(r)) { String msg = "startForegroundService() not allowed due to " + "mAllowStartForeground false: service " + r.shortInstanceName; @@ -1768,8 +1740,7 @@ public final class ActiveServices { if (!ignoreForeground) { logFgsBackgroundStart(r); - if (r.mAllowStartForeground == FGS_FEATURE_DENIED - && isBgFgsRestrictionEnabled(r)) { + if (r.mAllowStartForeground == REASON_DENIED && isBgFgsRestrictionEnabled(r)) { final String msg = "Service.startForeground() not allowed due to " + "mAllowStartForeground false: service " + r.shortInstanceName; @@ -2250,7 +2221,7 @@ public final class ActiveServices { psr.mAllowlistManager = false; for (int i = psr.numberOfRunningServices() - 1; i >= 0; i--) { ServiceRecord sr = psr.getRunningServiceAt(i); - if (sr.whitelistManager) { + if (sr.allowlistManager) { psr.mAllowlistManager = true; break; } @@ -2261,7 +2232,7 @@ public final class ActiveServices { final ProcessServiceRecord psr = service.app.mServices; psr.stopService(service); psr.updateBoundClientUids(); - if (service.whitelistManager) { + if (service.allowlistManager) { updateAllowlistManagerLocked(psr); } } @@ -2483,7 +2454,7 @@ public final class ActiveServices { clientPsr.setHasAboveClient(true); } if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) { - s.whitelistManager = true; + s.allowlistManager = true; } if ((flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) { s.setAllowedBgActivityStartsByBinding(true); @@ -2520,7 +2491,7 @@ public final class ActiveServices { if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) { servicePsr.setTreatLikeActivity(true); } - if (s.whitelistManager) { + if (s.allowlistManager) { servicePsr.mAllowlistManager = true; } // This could have made the service more important. @@ -2949,7 +2920,6 @@ public final class ActiveServices { final ServiceRestarter res = new ServiceRestarter(); r = new ServiceRecord(mAm, className, name, definingPackageName, definingUid, filter, sInfo, callingFromFg, res); - r.mRecentCallingPackage = callingPackage; res.setService(r); smap.mServicesByInstanceName.put(name, r); smap.mServicesByIntent.put(filter, r); @@ -2978,6 +2948,8 @@ public final class ActiveServices { } } if (r != null) { + r.mRecentCallingPackage = callingPackage; + r.mRecentCallingUid = callingUid; if (!mAm.validateAssociationAllowedLocked(callingPackage, callingUid, r.packageName, r.appInfo.uid)) { String msg = "association not allowed between packages " @@ -3440,12 +3412,13 @@ public final class ActiveServices { if (r.fgRequired) { if (DEBUG_FOREGROUND_SERVICE) { - Slog.v(TAG, "Whitelisting " + UserHandle.formatUid(r.appInfo.uid) + Slog.v(TAG, "Allowlisting " + UserHandle.formatUid(r.appInfo.uid) + " for fg-service launch"); } mAm.tempAllowlistUidLocked(r.appInfo.uid, - SERVICE_START_FOREGROUND_TIMEOUT, "fg-service-launch", - BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED); + SERVICE_START_FOREGROUND_TIMEOUT, PowerWhitelistManager.REASON_SERVICE_LAUNCH, + "fg-service-launch", TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, + r.mRecentCallingUid); } if (!mPendingServices.contains(r)) { @@ -3551,7 +3524,7 @@ public final class ActiveServices { } } - if (r.whitelistManager) { + if (r.allowlistManager) { psr.mAllowlistManager = true; } @@ -3941,11 +3914,11 @@ public final class ActiveServices { if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) { psr.updateHasAboveClientLocked(); } - // If this connection requested whitelist management, see if we should + // If this connection requested allowlist management, see if we should // now clear that state. if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) { - s.updateWhitelistManager(); - if (!s.whitelistManager && s.app != null) { + s.updateAllowlistManager(); + if (!s.allowlistManager && s.app != null) { updateAllowlistManagerLocked(s.app.mServices); } } @@ -5400,13 +5373,13 @@ public final class ActiveServices { } if (!r.mAllowWhileInUsePermissionInFgs - || (r.mAllowStartForeground == FGS_FEATURE_DENIED)) { - final @FgsFeatureRetCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked( + || (r.mAllowStartForeground == REASON_DENIED)) { + final @ReasonCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked( callingPackage, callingPid, callingUid, r, allowBackgroundActivityStarts); if (!r.mAllowWhileInUsePermissionInFgs) { - r.mAllowWhileInUsePermissionInFgs = (allowWhileInUse != FGS_FEATURE_DENIED); + r.mAllowWhileInUsePermissionInFgs = (allowWhileInUse != REASON_DENIED); } - if (r.mAllowStartForeground == FGS_FEATURE_DENIED) { + if (r.mAllowStartForeground == REASON_DENIED) { r.mAllowStartForeground = shouldAllowFgsStartForegroundLocked(allowWhileInUse, callingPackage, callingPid, callingUid, intent, r, allowBackgroundActivityStarts); @@ -5420,37 +5393,37 @@ public final class ActiveServices { * @param callingPackage caller app's package name. * @param callingUid caller app's uid. * @param r the service to start. - * @return {@link FgsFeatureRetCode} + * @return {@link ReasonCode} */ - private @FgsFeatureRetCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage, + private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage, int callingPid, int callingUid, ServiceRecord r, boolean allowBackgroundActivityStarts) { - int ret = FGS_FEATURE_DENIED; + int ret = REASON_DENIED; final int uidState = mAm.getUidStateLocked(callingUid); - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { // Is the calling UID at PROCESS_STATE_TOP or above? if (uidState <= PROCESS_STATE_TOP) { - ret = FGS_FEATURE_ALLOWED_BY_UID_STATE; + ret = getReasonCodeFromProcState(uidState); } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { // Does the calling UID have any visible activity? final boolean isCallingUidVisible = mAm.mAtmInternal.isUidForeground(callingUid); if (isCallingUidVisible) { - ret = FGS_FEATURE_ALLOWED_BY_UID_VISIBLE; + ret = REASON_UID_VISIBLE; } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { // Is the allow activity background start flag on? if (allowBackgroundActivityStarts) { - ret = FGS_FEATURE_ALLOWED_BY_FLAG; + ret = REASON_START_ACTIVITY_FLAG; } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { boolean isCallerSystem = false; final int callingAppId = UserHandle.getAppId(callingUid); switch (callingAppId) { @@ -5466,15 +5439,15 @@ public final class ActiveServices { } if (isCallerSystem) { - ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_UID; + ret = REASON_SYSTEM_UID; } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { final Integer allowedType = mAm.mProcessList.searchEachLruProcessesLOSP(false, pr -> { if (pr.uid == callingUid) { if (pr.getWindowProcessController().areBackgroundFgsStartsAllowed()) { - return FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER; + return REASON_ACTIVITY_STARTER; } } return null; @@ -5484,35 +5457,35 @@ public final class ActiveServices { } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { if (r.app != null) { ActiveInstrumentation instr = r.app.getActiveInstrumentation(); if (instr != null && instr.mHasBackgroundActivityStartsPermission) { - ret = FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION; + ret = REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION; } } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid) == PERMISSION_GRANTED) { - ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION; + ret = REASON_BACKGROUND_ACTIVITY_PERMISSION; } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { final boolean isAllowedPackage = mAllowListWhileInUsePermissionInFgs.contains(callingPackage); if (isAllowedPackage) { - ret = FGS_FEATURE_ALLOWED_BY_ALLOWLIST; + ret = REASON_ALLOWLISTED_PACKAGE; } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { // Is the calling UID a device owner app? final boolean isDeviceOwner = mAm.mInternal.isDeviceOwner(callingUid); if (isDeviceOwner) { - ret = FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER; + ret = REASON_DEVICE_OWNER; } } return ret; @@ -5527,38 +5500,40 @@ public final class ActiveServices { * @param callingUid caller app's uid. * @param intent intent to start/bind service. * @param r the service to start. - * @return {@link FgsFeatureRetCode} + * @return {@link ReasonCode} */ - private @FgsFeatureRetCode int shouldAllowFgsStartForegroundLocked( - @FgsFeatureRetCode int allowWhileInUse, String callingPackage, int callingPid, + private @ReasonCode int shouldAllowFgsStartForegroundLocked( + @ReasonCode int allowWhileInUse, String callingPackage, int callingPid, int callingUid, Intent intent, ServiceRecord r, boolean allowBackgroundActivityStarts) { int ret = allowWhileInUse; + FgsStartTempAllowList.TempFgsAllowListEntry tempAllowListReason = + r.mInfoTempFgsAllowListReason = mAm.isAllowlistedForFgsStartLOSP(callingUid); final StringBuilder sb = new StringBuilder(64); final int uidState = mAm.getUidStateLocked(callingUid); - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { // Is the calling UID at PROCESS_STATE_TOP or above? if (uidState <= PROCESS_STATE_TOP) { sb.append("uidState=").append(uidState); - ret = FGS_FEATURE_ALLOWED_BY_UID_STATE; + ret = getReasonCodeFromProcState(uidState); } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { final Integer allowedType = mAm.mProcessList.searchEachLruProcessesLOSP(false, app -> { if (app.uid == callingUid) { final ProcessStateRecord state = app.mState; - if (state.getAllowedStartFgs() != FGS_FEATURE_DENIED) { + if (state.getAllowedStartFgs() != REASON_DENIED) { return state.getAllowedStartFgs(); } else if (state.isAllowedStartFgsState()) { - return FGS_FEATURE_ALLOWED_BY_PROC_STATE; + return getReasonCodeFromProcState(state.getAllowStartFgsState()); } else if (state.areBackgroundFgsStartsAllowedByToken()) { - return FGS_FEATURE_ALLOWED_BY_FGS_BINDING; + return REASON_FGS_BINDING; } else { final ActiveInstrumentation instr = app.getActiveInstrumentation(); if (instr != null && instr.mHasBackgroundForegroundServiceStartsPermission) { - return FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION; + return REASON_INSTR_BACKGROUND_FGS_PERMISSION; } } } @@ -5569,55 +5544,59 @@ public final class ActiveServices { } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { if (mAm.checkPermission(START_FOREGROUND_SERVICES_FROM_BACKGROUND, callingPid, callingUid) == PERMISSION_GRANTED) { - ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION; + ret = REASON_BACKGROUND_FGS_PERMISSION; } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { if (mAm.checkPermission(SYSTEM_ALERT_WINDOW, callingPid, callingUid) == PERMISSION_GRANTED) { - ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION; + ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION; } } - if (ret == FGS_FEATURE_DENIED) { - if (mAm.isAllowlistedForFgsStartLOSP(callingUid)) { - // uid is on DeviceIdleController's user/system allowlist - // or AMS's FgsStartTempAllowList. - ret = FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST; + if (ret == REASON_DENIED) { + FgsStartTempAllowList.TempFgsAllowListEntry entry = + mAm.isAllowlistedForFgsStartLOSP(callingUid); + if (entry != null) { + if (entry == ActivityManagerService.FAKE_TEMP_ALLOWLIST_ENTRY) { + ret = REASON_SYSTEM_ALLOW_LISTED; + } else { + ret = entry.mReasonCode; + } } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { if (UserManager.isDeviceInDemoMode(mAm.mContext)) { - ret = FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE; + ret = REASON_DEVICE_DEMO_MODE; } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { // Is the calling UID a profile owner app? final boolean isProfileOwner = mAm.mInternal.isProfileOwner(callingUid); if (isProfileOwner) { - ret = FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER; + ret = REASON_PROFILE_OWNER; } } // NOTE this should always be the last check. - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { if (isPackageExemptedFromFgsRestriction(r.appInfo.packageName, r.appInfo.uid) || isPackageExemptedFromFgsRestriction(callingPackage, callingUid)) { - ret = FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES; + ret = REASON_EXEMPTED_PACKAGE; } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { final boolean isCompanionApp = mAm.mInternal.isAssociatedCompanionApp( UserHandle.getUserId(callingUid), callingUid); if (isCompanionApp) { - ret = FGS_FEATURE_ALLOWED_BY_COMPANION_APP; + ret = REASON_COMPANION_DEVICE_MANAGER; } } @@ -5626,7 +5605,15 @@ public final class ActiveServices { + "; callingUid: " + callingUid + "; uidState: " + ProcessList.makeProcStateString(uidState) + "; intent: " + intent - + "; code:" + fgsCodeToString(ret) + + "; code:" + reasonCodeToString(ret) + + "; tempAllowListReason:<" + + (tempAllowListReason == null ? null : + (tempAllowListReason.mReason + + ",reasonCode:" + + reasonCodeToString(tempAllowListReason.mReasonCode) + + ",duration:" + tempAllowListReason.mDuration + + ",callingUid:" + tempAllowListReason.mCallingUid)) + + ">" + "; extra:" + sb.toString() + "; targetSdkVersion:" + r.appInfo.targetSdkVersion + "]"; @@ -5660,62 +5647,11 @@ public final class ActiveServices { return CompatChanges.isChangeEnabled(FGS_BG_START_USE_EXEMPTION_LIST_CHANGE_ID, uid); } - static String fgsCodeToString(@FgsFeatureRetCode int code) { - switch (code) { - case FGS_FEATURE_DENIED: - return "DENIED"; - case FGS_FEATURE_ALLOWED_BY_UID_STATE: - return "ALLOWED_BY_UID_STATE"; - case FGS_FEATURE_ALLOWED_BY_PROC_STATE: - return "ALLOWED_BY_PROC_STATE"; - case FGS_FEATURE_ALLOWED_BY_UID_VISIBLE: - return "ALLOWED_BY_UID_VISIBLE"; - case FGS_FEATURE_ALLOWED_BY_FLAG: - return "ALLOWED_BY_FLAG"; - case FGS_FEATURE_ALLOWED_BY_SYSTEM_UID: - return "ALLOWED_BY_SYSTEM_UID"; - case FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION: - return "ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION"; - case FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION: - return "ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION"; - case FGS_FEATURE_ALLOWED_BY_ACTIVITY_TOKEN: - return "ALLOWED_BY_ACTIVITY_TOKEN"; - case FGS_FEATURE_ALLOWED_BY_FGS_TOKEN: - return "ALLOWED_BY_FGS_TOKEN"; - case FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION: - return "ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION"; - case FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION: - return "ALLOWED_BY_BACKGROUND_FGS_PERMISSION"; - case FGS_FEATURE_ALLOWED_BY_ALLOWLIST: - return "ALLOWED_BY_ALLOWLIST"; - case FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER: - return "ALLOWED_BY_DEVICE_OWNER"; - case FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST: - return "ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST"; - case FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION: - return "ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION"; - case FGS_FEATURE_ALLOWED_BY_FGS_BINDING: - return "ALLOWED_BY_FGS_BINDING"; - case FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE: - return "ALLOWED_BY_DEVICE_DEMO_MODE"; - case FGS_FEATURE_ALLOWED_BY_PROCESS_RECORD: - return "ALLOWED_BY_PROCESS_RECORD"; - case FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES: - return "FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES"; - case FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER: - return "ALLOWED_BY_ACTIVITY_STARTER"; - case FGS_FEATURE_ALLOWED_BY_COMPANION_APP: - return "ALLOWED_BY_COMPANION_APP"; - case FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER: - return "ALLOWED_BY_PROFILE_OWNER"; - default: - return ""; - } - } - - private static boolean isFgsBgStart(@FgsFeatureRetCode int code) { - return code != FGS_FEATURE_ALLOWED_BY_UID_STATE - && code != FGS_FEATURE_ALLOWED_BY_UID_VISIBLE; + private static boolean isFgsBgStart(@ReasonCode int code) { + return code != REASON_PROC_STATE_PERSISTENT + && code != REASON_PROC_STATE_PERSISTENT_UI + && code != REASON_PROC_STATE_TOP + && code != REASON_UID_VISIBLE; } // TODO: remove this notification after feature development is done @@ -5754,10 +5690,10 @@ public final class ActiveServices { } if (!r.mLoggedInfoAllowStartForeground) { final String msg = "Background started FGS: " - + ((r.mAllowStartForeground != FGS_FEATURE_DENIED) ? "Allowed " : "Disallowed ") + + ((r.mAllowStartForeground != REASON_DENIED) ? "Allowed " : "Disallowed ") + r.mInfoAllowStartForeground; Slog.wtfQuiet(TAG, msg); - if (r.mAllowStartForeground != FGS_FEATURE_DENIED) { + if (r.mAllowStartForeground != REASON_DENIED) { Slog.i(TAG, msg); } else { Slog.w(TAG, msg); diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java index 171b20c03689..9d1c83894d46 100644 --- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java +++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java @@ -68,7 +68,7 @@ class ActivityManagerDebugConfig { static final boolean DEBUG_UID_OBSERVERS = DEBUG_ALL || false; static final boolean DEBUG_USAGE_STATS = DEBUG_ALL || false; static final boolean DEBUG_PERMISSIONS_REVIEW = DEBUG_ALL || false; - static final boolean DEBUG_WHITELISTS = DEBUG_ALL || false; + static final boolean DEBUG_ALLOWLISTS = DEBUG_ALL || false; static final String POSTFIX_BACKUP = (APPEND_CATEGORY_NAME) ? "_Backup" : ""; static final String POSTFIX_BROADCAST = (APPEND_CATEGORY_NAME) ? "_Broadcast" : ""; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 29b85acc6825..5e61f94826c1 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -33,7 +33,6 @@ import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.app.AppOpsManager.OP_NONE; -import static android.app.BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED; import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT; import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES; import static android.content.pm.PackageManager.MATCH_ALL; @@ -50,8 +49,12 @@ import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; import static android.os.IServiceManager.DUMP_FLAG_PROTO; +import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALLOW_LISTED; +import static android.os.PowerWhitelistManager.REASON_UNKNOWN; +import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED; import static android.os.Process.BLUETOOTH_UID; import static android.os.Process.FIRST_APPLICATION_UID; +import static android.os.Process.INVALID_UID; import static android.os.Process.NETWORK_STACK_UID; import static android.os.Process.NFC_UID; import static android.os.Process.PHONE_UID; @@ -101,7 +104,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_WHITELISTS; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALLOWLISTS; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP; @@ -142,6 +145,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; @@ -251,6 +256,7 @@ import android.os.ParcelFileDescriptor; import android.os.PowerManager; import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; +import android.os.PowerWhitelistManager.ReasonCode; import android.os.PowerWhitelistManager.TempAllowListType; import android.os.Process; import android.os.RemoteCallback; @@ -305,6 +311,7 @@ import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; import com.android.internal.app.ProcessMap; import com.android.internal.app.SystemUserHomeActivity; +import com.android.internal.app.procstats.ProcessState; import com.android.internal.app.procstats.ProcessStats; import com.android.internal.content.PackageHelper; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; @@ -622,7 +629,7 @@ public class ActivityManagerService extends IActivityManager.Stub */ private volatile String mDeviceOwnerName; - private volatile int mDeviceOwnerUid = Process.INVALID_UID; + private volatile int mDeviceOwnerUid = INVALID_UID; /** * Map userId to its companion app uids. @@ -1148,13 +1155,13 @@ public class ActivityManagerService extends IActivityManager.Stub DeviceIdleInternal mLocalDeviceIdleController; /** - * Power-save whitelisted app-ids (not including except-idle-whitelisted ones). + * Power-save allowlisted app-ids (not including except-idle-allowlisted ones). */ @CompositeRWLock({"this", "mProcLock"}) int[] mDeviceIdleAllowlist = new int[0]; /** - * Power-save whitelisted app-ids (including except-idle-whitelisted ones). + * Power-save allowlisted app-ids (including except-idle-allowlisted ones). */ @CompositeRWLock({"this", "mProcLock"}) int[] mDeviceIdleExceptIdleAllowlist = new int[0]; @@ -1170,20 +1177,27 @@ public class ActivityManagerService extends IActivityManager.Stub final long duration; final String tag; final int type; + final @ReasonCode int reasonCode; - PendingTempAllowlist(int targetUid, long duration, String tag, int type) { + PendingTempAllowlist(int targetUid, long duration, @ReasonCode int reasonCode, String tag, + int type) { this.targetUid = targetUid; this.duration = duration; this.tag = tag; this.type = type; + this.reasonCode = reasonCode; } void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); - proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TARGET_UID, targetUid); - proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.DURATION_MS, duration); + proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TARGET_UID, + targetUid); + proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.DURATION_MS, + duration); proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TAG, tag); proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TYPE, type); + proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.REASON_CODE, + reasonCode); proto.end(token); } } @@ -1197,6 +1211,9 @@ public class ActivityManagerService extends IActivityManager.Stub @CompositeRWLock({"this", "mProcLock"}) final FgsStartTempAllowList mFgsStartTempAllowList = new FgsStartTempAllowList(); + static final FgsStartTempAllowList.TempFgsAllowListEntry FAKE_TEMP_ALLOWLIST_ENTRY = new + FgsStartTempAllowList.TempFgsAllowListEntry(Long.MAX_VALUE, Long.MAX_VALUE, + REASON_SYSTEM_ALLOW_LISTED, "", INVALID_UID); /** * Information about and control over application operations */ @@ -1818,6 +1835,15 @@ public class ActivityManagerService extends IActivityManager.Stub ncl.start(); } + /** + * Sets a policy for handling app ops. + * + * @param appOpsPolicy The policy. + */ + public void setAppOpsPolicy(@Nullable CheckOpsDelegate appOpsPolicy) { + mAppOpsService.setAppOpsPolicy(appOpsPolicy); + } + public IAppOpsService getAppOpsService() { return mAppOpsService; } @@ -3777,10 +3803,11 @@ public class ActivityManagerService extends IActivityManager.Stub mi.getTotalUss(), mi.getTotalRss(), false, ProcessStats.ADD_PSS_EXTERNAL_SLOW, duration); proc.getPkgList().forEachPackageProcessStats(holder -> { + final ProcessState state = holder.state; FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED, proc.info.uid, - holder.state.getName(), - holder.state.getPackage(), + state != null ? state.getName() : proc.processName, + state != null ? state.getPackage() : proc.info.packageName, mi.getTotalPss(), mi.getTotalUss(), mi.getTotalRss(), @@ -4816,12 +4843,12 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public int sendIntentSender(IIntentSender target, IBinder whitelistToken, int code, + public int sendIntentSender(IIntentSender target, IBinder allowlistToken, int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { if (target instanceof PendingIntentRecord) { return ((PendingIntentRecord)target).sendWithResult(code, intent, resolvedType, - whitelistToken, finishedReceiver, requiredPermission, options); + allowlistToken, finishedReceiver, requiredPermission, options); } else { if (intent == null) { // Weird case: someone has given us their own custom IIntentSender, and now @@ -4833,7 +4860,7 @@ public class ActivityManagerService extends IActivityManager.Stub intent = new Intent(Intent.ACTION_MAIN); } try { - target.send(code, intent, resolvedType, whitelistToken, null, + target.send(code, intent, resolvedType, allowlistToken, null, requiredPermission, options); } catch (RemoteException e) { } @@ -4858,19 +4885,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); } @@ -4882,15 +4896,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 @@ -4916,15 +4932,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; @@ -4941,33 +4948,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()"); @@ -5448,7 +5428,7 @@ public class ActivityManagerService extends IActivityManager.Stub } switch (appop) { case AppOpsManager.MODE_ALLOWED: - // If force-background-check is enabled, restrict all apps that aren't whitelisted. + // If force-background-check is enabled, restrict all apps that aren't allowlisted. if (mForceBackgroundCheck && !UserHandle.isCore(uid) && !isOnDeviceIdleAllowlistLOSP(uid, /*allowExceptIdleToo=*/ true)) { @@ -5484,7 +5464,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (uidOnBackgroundAllowlistLOSP(uid)) { if (DEBUG_BACKGROUND_CHECK) { Slog.i(TAG, "App " + uid + "/" + packageName - + " on background whitelist; not restricted in background"); + + " on background allowlist; not restricted in background"); } return ActivityManager.APP_START_MODE_NORMAL; } @@ -5493,7 +5473,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (isOnDeviceIdleAllowlistLOSP(uid, /*allowExceptIdleToo=*/ false)) { if (DEBUG_BACKGROUND_CHECK) { Slog.i(TAG, "App " + uid + "/" + packageName - + " on idle whitelist; not restricted in background"); + + " on idle allowlist; not restricted in background"); } return ActivityManager.APP_START_MODE_NORMAL; } @@ -5581,10 +5561,19 @@ public class ActivityManagerService extends IActivityManager.Stub || mPendingTempAllowlist.indexOfKey(uid) >= 0; } + /** + * Is the uid allowlisted to start FGS? + * @param uid + * @return a TempAllowListEntry if the uid is allowed. + * null if the uid is not allowed. + */ + @Nullable @GuardedBy(anyOf = {"this", "mProcLock"}) - boolean isAllowlistedForFgsStartLOSP(int uid) { - return Arrays.binarySearch(mDeviceIdleExceptIdleAllowlist, UserHandle.getAppId(uid)) >= 0 - || mFgsStartTempAllowList.isAllowed(uid); + FgsStartTempAllowList.TempFgsAllowListEntry isAllowlistedForFgsStartLOSP(int uid) { + if (Arrays.binarySearch(mDeviceIdleExceptIdleAllowlist, UserHandle.getAppId(uid)) >= 0) { + return FAKE_TEMP_ALLOWLIST_ENTRY; + } + return mFgsStartTempAllowList.getAllowedDurationAndReason(uid); } /** @@ -6056,13 +6045,13 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void backgroundWhitelistUid(final int uid) { + public void backgroundAllowlistUid(final int uid) { if (Binder.getCallingUid() != Process.SYSTEM_UID) { - throw new SecurityException("Only the OS may call backgroundWhitelistUid()"); + throw new SecurityException("Only the OS may call backgroundAllowlistUid()"); } if (DEBUG_BACKGROUND_CHECK) { - Slog.i(TAG, "Adding uid " + uid + " to bg uid whitelist"); + Slog.i(TAG, "Adding uid " + uid + " to bg uid allowlist"); } synchronized (this) { synchronized (mProcLock) { @@ -6632,6 +6621,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)) { @@ -7247,6 +7248,9 @@ public class ActivityManagerService extends IActivityManager.Stub final long memoryGrowthThreshold = Math.max(totalMemoryInKb / 100, MINIMUM_MEMORY_GROWTH_THRESHOLD); mProcessList.forEachLruProcessesLOSP(false, proc -> { + if (proc.getThread() == null) { + return; + } final ProcessProfileRecord pr = proc.mProfile; final ProcessStateRecord state = proc.mState; final int setProcState = state.getSetProcState(); @@ -8290,7 +8294,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (!TextUtils.isEmpty(packageName)) { final int uid = enforceDumpPermissionForPackage(packageName, userId, callingUid, "getHistoricalProcessExitReasons"); - if (uid != Process.INVALID_UID) { + if (uid != INVALID_UID) { mProcessList.mAppExitInfoTracker.getExitInfo( packageName, uid, pid, maxNum, results); tombstoneService.collectTombstones(results, uid, pid, maxNum); @@ -8324,7 +8328,7 @@ public class ActivityManagerService extends IActivityManager.Stub int enforceDumpPermissionForPackage(String packageName, int userId, int callingUid, String function) { final long identity = Binder.clearCallingIdentity(); - int uid = Process.INVALID_UID; + int uid = INVALID_UID; try { uid = mPackageManagerInt.getPackageUid(packageName, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId); @@ -9216,6 +9220,8 @@ public class ActivityManagerService extends IActivityManager.Stub pw.println(ptw.tag); pw.print(" "); pw.print(ptw.type); + pw.print(" "); + pw.print(ptw.reasonCode); } } } @@ -10077,6 +10083,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 @@ -10084,7 +10091,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" @@ -10092,7 +10099,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" @@ -10400,6 +10407,8 @@ public class ActivityManagerService extends IActivityManager.Stub } endTime = SystemClock.currentThreadTimeMillis(); hasSwapPss = mi.hasSwappedOutPss; + memtrackGraphics = mi.getOtherPrivate(Debug.MemoryInfo.OTHER_GRAPHICS); + memtrackGl = mi.getOtherPrivate(Debug.MemoryInfo.OTHER_GL); } else { reportType = ProcessStats.ADD_PSS_EXTERNAL; startTime = SystemClock.currentThreadTimeMillis(); @@ -10551,6 +10560,8 @@ public class ActivityManagerService extends IActivityManager.Stub if (!Debug.getMemoryInfo(st.pid, info)) { return; } + memtrackGraphics = info.getOtherPrivate(Debug.MemoryInfo.OTHER_GRAPHICS); + memtrackGl = info.getOtherPrivate(Debug.MemoryInfo.OTHER_GL); } else { long pss = Debug.getPss(st.pid, tmpLong, memtrackTmp); if (pss == 0) { @@ -12568,7 +12579,7 @@ public class ActivityManagerService extends IActivityManager.Stub BroadcastOptions brOptions = null; if (bOptions != null) { brOptions = new BroadcastOptions(bOptions); - if (brOptions.getTemporaryAppWhitelistDuration() > 0) { + if (brOptions.getTemporaryAppAllowlistDuration() > 0) { // See if the caller is allowed to do this. Note we are checking against // the actual real caller (not whoever provided the operation as say a // PendingIntent), because that who is actually supplied the arguments. @@ -13950,7 +13961,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) { @@ -14001,6 +14012,9 @@ public class ActivityManagerService extends IActivityManager.Stub final long uptimeSince = curUptime - mLastPowerCheckUptime; mLastPowerCheckUptime = curUptime; mProcessList.forEachLruProcessesLOSP(false, app -> { + if (app.getThread() == null) { + return; + } if (app.mState.getSetProcState() >= ActivityManager.PROCESS_STATE_HOME) { int cpuLimit; long checkDur = curUptime - app.mState.getWhenUnimportant(); @@ -14102,11 +14116,12 @@ public class ActivityManagerService extends IActivityManager.Stub mBatteryStatsService.reportExcessiveCpu(app.info.uid, app.processName, uptimeSince, cputimeUsed); app.getPkgList().forEachPackageProcessStats(holder -> { + final ProcessState state = holder.state; FrameworkStatsLog.write( FrameworkStatsLog.EXCESSIVE_CPU_USAGE_REPORTED, app.info.uid, processName, - holder.state.getPackage(), + state != null ? state.getPackage() : app.info.packageName, holder.appVersion); }); return true; @@ -14446,8 +14461,8 @@ public class ActivityManagerService extends IActivityManager.Stub */ @GuardedBy("this") void tempAllowlistForPendingIntentLocked(int callerPid, int callerUid, int targetUid, - long duration, int type, String tag) { - if (DEBUG_WHITELISTS) { + long duration, int type, @ReasonCode int reasonCode, String reason) { + if (DEBUG_ALLOWLISTS) { Slog.d(TAG, "tempAllowlistForPendingIntentLocked(" + callerPid + ", " + callerUid + ", " + targetUid + ", " + duration + ", " + type + ")"); } @@ -14466,7 +14481,7 @@ public class ActivityManagerService extends IActivityManager.Stub != PackageManager.PERMISSION_GRANTED && checkPermission(START_FOREGROUND_SERVICES_FROM_BACKGROUND, callerPid, callerUid) != PackageManager.PERMISSION_GRANTED) { - if (DEBUG_WHITELISTS) { + if (DEBUG_ALLOWLISTS) { Slog.d(TAG, "tempAllowlistForPendingIntentLocked() for target " + targetUid + ": pid " + callerPid + " is not allowed"); } @@ -14475,22 +14490,23 @@ public class ActivityManagerService extends IActivityManager.Stub } } - tempAllowlistUidLocked(targetUid, duration, tag, type); + tempAllowlistUidLocked(targetUid, duration, reasonCode, reason, type, callerUid); } /** * Allowlists {@code targetUid} to temporarily bypass Power Save mode. */ @GuardedBy("this") - void tempAllowlistUidLocked(int targetUid, long duration, String tag, int type) { + void tempAllowlistUidLocked(int targetUid, long duration, @ReasonCode int reasonCode, + String reason, int type, int callingUid) { synchronized (mProcLock) { mPendingTempAllowlist.put(targetUid, - new PendingTempAllowlist(targetUid, duration, tag, type)); + new PendingTempAllowlist(targetUid, duration, reasonCode, reason, type)); setUidTempAllowlistStateLSP(targetUid, true); mUiHandler.obtainMessage(PUSH_TEMP_ALLOWLIST_UI_MSG).sendToTarget(); - if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) { - mFgsStartTempAllowList.add(targetUid, duration); + if (type == TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED) { + mFgsStartTempAllowList.add(targetUid, duration, reasonCode, reason, callingUid); } } } @@ -14516,7 +14532,7 @@ public class ActivityManagerService extends IActivityManager.Stub for (int i = 0; i < N; i++) { PendingTempAllowlist ptw = list[i]; mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid, - ptw.duration, ptw.type, true, ptw.tag); + ptw.duration, ptw.type, true, ptw.reasonCode, ptw.tag); } } @@ -15104,10 +15120,17 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void setPendingIntentWhitelistDuration(IIntentSender target, IBinder whitelistToken, + public void setPendingIntentAllowlistDuration(IIntentSender target, IBinder allowlistToken, + long duration, int type, @ReasonCode int reasonCode, @Nullable String reason) { + mPendingIntentController.setPendingIntentAllowlistDuration(target, allowlistToken, + duration, type, reasonCode, reason); + } + + @Override + public void setPendingIntentWhitelistDuration(IIntentSender target, IBinder allowlistToken, long duration, int type) { - mPendingIntentController.setPendingIntentWhitelistDuration(target, whitelistToken, - duration, type); + mPendingIntentController.setPendingIntentAllowlistDuration(target, allowlistToken, + duration, type, REASON_UNKNOWN, ""); } @Override @@ -15117,32 +15140,32 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void setPendingIntentAllowBgActivityStarts(IIntentSender target, - IBinder whitelistToken, int flags) { + IBinder allowlistToken, int flags) { if (!(target instanceof PendingIntentRecord)) { Slog.w(TAG, "setPendingIntentAllowBgActivityStarts():" + " not a PendingIntentRecord: " + target); return; } synchronized (ActivityManagerService.this) { - ((PendingIntentRecord) target).setAllowBgActivityStarts(whitelistToken, flags); + ((PendingIntentRecord) target).setAllowBgActivityStarts(allowlistToken, flags); } } @Override public void clearPendingIntentAllowBgActivityStarts(IIntentSender target, - IBinder whitelistToken) { + IBinder allowlistToken) { if (!(target instanceof PendingIntentRecord)) { Slog.w(TAG, "clearPendingIntentAllowBgActivityStarts():" + " not a PendingIntentRecord: " + target); return; } synchronized (ActivityManagerService.this) { - ((PendingIntentRecord) target).clearAllowBgActivityStarts(whitelistToken); + ((PendingIntentRecord) target).clearAllowBgActivityStarts(allowlistToken); } } @Override - public void setDeviceIdleWhitelist(int[] allAppids, int[] exceptIdleAppids) { + public void setDeviceIdleAllowlist(int[] allAppids, int[] exceptIdleAppids) { synchronized (ActivityManagerService.this) { synchronized (mProcLock) { mDeviceIdleAllowlist = allAppids; @@ -15152,17 +15175,19 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void updateDeviceIdleTempWhitelist(int[] appids, int changingUid, boolean adding, - long durationMs, @TempAllowListType int type) { + public void updateDeviceIdleTempAllowlist(int[] appids, int changingUid, boolean adding, + long durationMs, @TempAllowListType int type, @ReasonCode int reasonCode, + @Nullable String reason, int callingUid) { synchronized (ActivityManagerService.this) { synchronized (mProcLock) { mDeviceIdleTempAllowlist = appids; if (adding) { - if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) { - mFgsStartTempAllowList.add(changingUid, durationMs); + if (type == TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED) { + mFgsStartTempAllowList.add(changingUid, durationMs, reasonCode, reason, + callingUid); } + setAppIdTempAllowlistStateLSP(changingUid, adding); } - setAppIdTempAllowlistStateLSP(changingUid, adding); } } } @@ -15523,11 +15548,11 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid, - long duration, int type, String tag) { + public void tempAllowlistForPendingIntent(int callerPid, int callerUid, int targetUid, + long duration, int type, @ReasonCode int reasonCode, String reason) { synchronized (ActivityManagerService.this) { ActivityManagerService.this.tempAllowlistForPendingIntentLocked( - callerPid, callerUid, targetUid, duration, type, tag); + callerPid, callerUid, targetUid, duration, type, reasonCode, reason); } } diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java index 48222cb075cd..a9e557103966 100644 --- a/services/core/java/com/android/server/am/AppErrorDialog.java +++ b/services/core/java/com/android/server/am/AppErrorDialog.java @@ -18,10 +18,7 @@ package com.android.server.am; import static android.app.ActivityTaskManager.INVALID_TASK_ID; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.res.Resources; import android.os.Build; import android.os.Bundle; @@ -138,19 +135,6 @@ final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListen findViewById(com.android.internal.R.id.customPanel).setVisibility(View.VISIBLE); } - @Override - public void onStart() { - super.onStart(); - getContext().registerReceiver(mReceiver, - new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - } - - @Override - protected void onStop() { - super.onStop(); - getContext().unregisterReceiver(mReceiver); - } - private final Handler mHandler = new Handler() { public void handleMessage(Message msg) { setResult(msg.what); @@ -204,15 +188,6 @@ final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListen } } - private final BroadcastReceiver mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) { - cancel(); - } - } - }; - static class Data { AppErrorResult result; int taskId; diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index e5a5cff409b3..3602f44cd785 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -29,6 +29,7 @@ import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_N import android.app.ActivityManager; import android.app.ActivityOptions; +import android.app.AnrController; import android.app.ApplicationErrorReport; import android.app.ApplicationExitInfo; import android.content.ActivityNotFoundException; @@ -1058,7 +1059,26 @@ class AppErrors { Settings.Secure.ANR_SHOW_BACKGROUND, 0, mService.mUserController.getCurrentUserId()) != 0; if (mService.mAtmInternal.canShowErrorDialogs() || showBackground) { - errState.getDialogController().showAnrDialogs(data); + AnrController anrController = errState.getDialogController().getAnrController(); + if (anrController == null) { + errState.getDialogController().showAnrDialogs(data); + } else { + String packageName = proc.info.packageName; + int uid = proc.info.uid; + boolean showDialog = anrController.onAnrDelayCompleted(packageName, uid); + + if (showDialog) { + Slog.d(TAG, "ANR delay completed. Showing ANR dialog for package: " + + packageName); + errState.getDialogController().showAnrDialogs(data); + } else { + Slog.d(TAG, "ANR delay completed. Cancelling ANR dialog for package: " + + packageName); + errState.setNotResponding(false); + errState.setNotRespondingReport(null); + errState.getDialogController().clearAnrDialogs(); + } + } } else { MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR, AppNotRespondingDialog.CANT_SHOW); diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java index 77d2898842c6..b233a2ccc6e3 100644 --- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java +++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java @@ -181,6 +181,11 @@ final class AppNotRespondingDialog extends BaseErrorDialog implements View.OnCli } }; + @Override + protected void closeDialog() { + mHandler.obtainMessage(FORCE_CLOSE).sendToTarget(); + } + static class Data { final ProcessRecord proc; final ApplicationInfo aInfo; diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java index c8630fa52973..f8494d8a7c04 100644 --- a/services/core/java/com/android/server/am/AppProfiler.java +++ b/services/core/java/com/android/server/am/AppProfiler.java @@ -1327,6 +1327,8 @@ public class AppProfiler { // Get a list of Stats that have vsize > 0 final List<ProcessCpuTracker.Stats> stats = getCpuStats(st -> st.vsize > 0); final int statsCount = stats.size(); + long totalMemtrackGraphics = 0; + long totalMemtrackGl = 0; for (int i = 0; i < statsCount; i++) { ProcessCpuTracker.Stats st = stats.get(i); long pss = Debug.getPss(st.pid, swaptrackTmp, memtrackTmp); @@ -1337,6 +1339,8 @@ public class AppProfiler { mi.pss = pss; mi.swapPss = swaptrackTmp[1]; mi.memtrack = memtrackTmp[0]; + totalMemtrackGraphics += memtrackTmp[1]; + totalMemtrackGl += memtrackTmp[2]; memInfos.add(mi); } } @@ -1345,20 +1349,18 @@ public class AppProfiler { long totalPss = 0; long totalSwapPss = 0; long totalMemtrack = 0; - long totalMemtrackGraphics = 0; - long totalMemtrackGl = 0; for (int i = 0, size = memInfos.size(); i < size; i++) { ProcessMemInfo mi = memInfos.get(i); if (mi.pss == 0) { mi.pss = Debug.getPss(mi.pid, swaptrackTmp, memtrackTmp); mi.swapPss = swaptrackTmp[1]; mi.memtrack = memtrackTmp[0]; + totalMemtrackGraphics += memtrackTmp[1]; + totalMemtrackGl += memtrackTmp[2]; } totalPss += mi.pss; totalSwapPss += mi.swapPss; totalMemtrack += mi.memtrack; - totalMemtrackGraphics += memtrackTmp[1]; - totalMemtrackGl += memtrackTmp[2]; } Collections.sort(memInfos, new Comparator<ProcessMemInfo>() { @Override public int compare(ProcessMemInfo lhs, ProcessMemInfo rhs) { diff --git a/services/core/java/com/android/server/am/AppWaitingForDebuggerDialog.java b/services/core/java/com/android/server/am/AppWaitingForDebuggerDialog.java index 3ce24712b42f..262e79521e8c 100644 --- a/services/core/java/com/android/server/am/AppWaitingForDebuggerDialog.java +++ b/services/core/java/com/android/server/am/AppWaitingForDebuggerDialog.java @@ -61,6 +61,11 @@ final class AppWaitingForDebuggerDialog extends BaseErrorDialog { public void onStop() { } + @Override + protected void closeDialog() { + /* Do nothing */ + } + private final Handler mHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { diff --git a/services/core/java/com/android/server/am/BaseErrorDialog.java b/services/core/java/com/android/server/am/BaseErrorDialog.java index aabb5877764e..7b5f2cd78947 100644 --- a/services/core/java/com/android/server/am/BaseErrorDialog.java +++ b/services/core/java/com/android/server/am/BaseErrorDialog.java @@ -16,16 +16,19 @@ package com.android.server.am; -import com.android.internal.R; - import android.app.AlertDialog; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.os.Handler; import android.os.Message; import android.view.KeyEvent; import android.view.WindowManager; import android.widget.Button; +import com.android.internal.R; + public class BaseErrorDialog extends AlertDialog { private static final int ENABLE_BUTTONS = 0; private static final int DISABLE_BUTTONS = 1; @@ -44,10 +47,19 @@ public class BaseErrorDialog extends AlertDialog { getWindow().setAttributes(attrs); } + @Override public void onStart() { super.onStart(); mHandler.sendEmptyMessage(DISABLE_BUTTONS); mHandler.sendMessageDelayed(mHandler.obtainMessage(ENABLE_BUTTONS), 1000); + getContext().registerReceiver(mReceiver, + new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); + } + + @Override + protected void onStop() { + super.onStop(); + getContext().unregisterReceiver(mReceiver); } public boolean dispatchKeyEvent(KeyEvent event) { @@ -84,4 +96,24 @@ public class BaseErrorDialog extends AlertDialog { } } }; + + /** + * Called when received ACTION_CLOSE_SYSTEM_DIALOGS. + */ + protected void closeDialog() { + if (mCancelable) { + cancel(); + } else { + dismiss(); + } + } + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) { + closeDialog(); + } + } + }; } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 34ff77494f94..29061930cd84 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -22,6 +22,7 @@ import static android.text.TextUtils.formatSimple; import static com.android.server.am.ActivityManagerDebugConfig.*; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.AppGlobals; import android.app.AppOpsManager; @@ -43,6 +44,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.PowerWhitelistManager; import android.os.PowerWhitelistManager.TempAllowListType; import android.os.Process; import android.os.RemoteException; @@ -903,8 +905,9 @@ public final class BroadcastQueue { return false; } - final void scheduleTempWhitelistLocked(int uid, long duration, BroadcastRecord r, - @TempAllowListType int type) { + final void scheduleTempAllowlistLocked(int uid, long duration, BroadcastRecord r, + @TempAllowListType int type, @PowerWhitelistManager.ReasonCode int reasonCode, + @Nullable String reason) { if (duration > Integer.MAX_VALUE) { duration = Integer.MAX_VALUE; } @@ -926,10 +929,11 @@ public final class BroadcastQueue { b.append(r.intent.getData()); } if (DEBUG_BROADCAST) { - Slog.v(TAG, "Broadcast temp whitelist uid=" + uid + " duration=" + duration + Slog.v(TAG, "Broadcast temp allowlist uid=" + uid + " duration=" + duration + " type=" + type + " : " + b.toString()); } - mService.tempAllowlistUidLocked(uid, duration, b.toString(), type); + mService.tempAllowlistUidLocked(uid, duration, reasonCode, b.toString(), type, + r.callingUid); } /** @@ -1332,10 +1336,12 @@ public final class BroadcastQueue { // r is guaranteed ordered at this point, so we know finishReceiverLocked() // will get a callback and handle the activity start token lifecycle. } - if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) { - scheduleTempWhitelistLocked(filter.owningUid, - brOptions.getTemporaryAppWhitelistDuration(), r, - brOptions.getTemporaryAppWhitelistType()); + if (brOptions != null && brOptions.getTemporaryAppAllowlistDuration() > 0) { + scheduleTempAllowlistLocked(filter.owningUid, + brOptions.getTemporaryAppAllowlistDuration(), r, + brOptions.getTemporaryAppAllowlistType(), + brOptions.getTemporaryAppAllowlistReasonCode(), + brOptions.getTemporaryAppAllowlistReason()); } } return; @@ -1619,11 +1625,13 @@ public final class BroadcastQueue { } final boolean isActivityCapable = - (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0); + (brOptions != null && brOptions.getTemporaryAppAllowlistDuration() > 0); if (isActivityCapable) { - scheduleTempWhitelistLocked(receiverUid, - brOptions.getTemporaryAppWhitelistDuration(), r, - brOptions.getTemporaryAppWhitelistType()); + scheduleTempAllowlistLocked(receiverUid, + brOptions.getTemporaryAppAllowlistDuration(), r, + brOptions.getTemporaryAppAllowlistType(), + brOptions.getTemporaryAppAllowlistReasonCode(), + brOptions.getTemporaryAppAllowlistReason()); } // Broadcast is being executed, its package can't be stopped. diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index a34163cd5fa2..c5f082a0f9e3 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -536,18 +536,6 @@ public final class CachedAppOptimizer { } /** - * Enable or disable the freezer. When enable == false all frozen processes are unfrozen, - * but aren't removed from the freezer. While in this state, processes can be added or removed - * by using Process.setProcessFrozen(), but they wouldn't be actually frozen until the freezer - * is enabled. If enable == true all processes in the freezer are frozen. - * - * @param enable Specify whether to enable (true) or disable (false) the freezer. - * - * @hide - */ - private static native void enableFreezerInternal(boolean enable); - - /** * Informs binder that a process is about to be frozen. If freezer is enabled on a process via * this method, this method will synchronously dispatch all pending transactions to the * specified pid. This method will not add significant latencies when unfreezing. @@ -590,10 +578,6 @@ public final class CachedAppOptimizer { if (state == '1' || state == '0') { supported = true; - // This is a workaround after reverting the cgroup v2 uid/pid hierarchy due to - // http://b/179006802. - // TODO: remove once the uid/pid hierarchy is restored - enableFreezerInternal(true); } else { Slog.e(TAG_AM, "unexpected value in cgroup.freeze"); } diff --git a/services/core/java/com/android/server/am/ErrorDialogController.java b/services/core/java/com/android/server/am/ErrorDialogController.java index ef135d55e43c..f23d309e424a 100644 --- a/services/core/java/com/android/server/am/ErrorDialogController.java +++ b/services/core/java/com/android/server/am/ErrorDialogController.java @@ -16,6 +16,8 @@ package com.android.server.am; +import android.annotation.Nullable; +import android.app.AnrController; import android.app.Dialog; import android.content.Context; @@ -57,6 +59,13 @@ final class ErrorDialogController { @GuardedBy("mProcLock") private AppWaitingForDebuggerDialog mWaitDialog; + /** + * ANR dialog controller + */ + @GuardedBy("mProcLock") + @Nullable + private AnrController mAnrController; + @GuardedBy("mProcLock") boolean hasCrashDialogs() { return mCrashDialogs != null; @@ -118,6 +127,7 @@ final class ErrorDialogController { } forAllDialogs(mAnrDialogs, Dialog::dismiss); mAnrDialogs = null; + mAnrController = null; } @GuardedBy("mProcLock") @@ -220,6 +230,17 @@ final class ErrorDialogController { }); } + @GuardedBy("mProcLock") + @Nullable + AnrController getAnrController() { + return mAnrController; + } + + @GuardedBy("mProcLock") + void setAnrController(AnrController controller) { + mAnrController = controller; + } + /** * Helper function to collect contexts from crashed app located displays. * diff --git a/services/core/java/com/android/server/am/FgsStartTempAllowList.java b/services/core/java/com/android/server/am/FgsStartTempAllowList.java index 4d8749c05294..1f897b5928ce 100644 --- a/services/core/java/com/android/server/am/FgsStartTempAllowList.java +++ b/services/core/java/com/android/server/am/FgsStartTempAllowList.java @@ -18,24 +18,53 @@ package com.android.server.am; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; +import android.annotation.Nullable; +import android.os.PowerWhitelistManager.ReasonCode; import android.os.SystemClock; import android.util.Slog; -import android.util.SparseLongArray; +import android.util.SparseArray; /** * List of uids that are temporarily allowed to start FGS from background. */ final class FgsStartTempAllowList { private static final int MAX_SIZE = 100; + + public static final class TempFgsAllowListEntry { + final long mExpirationTime; + final long mDuration; + final @ReasonCode int mReasonCode; + final String mReason; + final int mCallingUid; + + TempFgsAllowListEntry(long expirationTime, long duration, @ReasonCode int reasonCode, + String reason, int callingUid) { + mExpirationTime = expirationTime; + mDuration = duration; + mReasonCode = reasonCode; + mReason = reason; + mCallingUid = callingUid; + } + } + /** - * The key is the uid, the value is expiration elapse time in ms of this temp-allowed uid. + * The key is the uid, the value is a TempAllowListEntry. */ - private final SparseLongArray mTempAllowListFgs = new SparseLongArray(); + private final SparseArray<TempFgsAllowListEntry> mTempAllowListFgs = new SparseArray<>(); FgsStartTempAllowList() { } - void add(int uid, long duration) { + /** + * Add a uid and its duration with reason into the FGS temp-allowlist. + * @param uid + * @param duration temp-allowlisted duration in milliseconds. + * @param reason A human-readable reason for logging purposes. + * @param callingUid the callingUid that setup this temp allowlist, only valid when param adding + * is true. + */ + void add(int uid, long duration, @ReasonCode int reasonCode, @Nullable String reason, + int callingUid) { if (duration <= 0) { Slog.e(TAG_AM, "FgsStartTempAllowList bad duration:" + duration + " uid: " + uid); @@ -48,26 +77,36 @@ final class FgsStartTempAllowList { } final long now = SystemClock.elapsedRealtime(); for (int index = mTempAllowListFgs.size() - 1; index >= 0; index--) { - if (mTempAllowListFgs.valueAt(index) < now) { + if (mTempAllowListFgs.valueAt(index).mExpirationTime < now) { mTempAllowListFgs.removeAt(index); } } - final long existingExpirationTime = mTempAllowListFgs.get(uid, -1); + final TempFgsAllowListEntry existing = mTempAllowListFgs.get(uid); final long expirationTime = now + duration; - if (existingExpirationTime == -1 || existingExpirationTime < expirationTime) { - mTempAllowListFgs.put(uid, expirationTime); + if (existing == null || existing.mExpirationTime < expirationTime) { + mTempAllowListFgs.put(uid, + new TempFgsAllowListEntry(expirationTime, duration, reasonCode, + reason == null ? "" : reason, callingUid)); } } - boolean isAllowed(int uid) { + /** + * Is this uid temp-allowlisted to start FGS. + * @param uid + * @return If uid is in the temp-allowlist, return the {@link TempFgsAllowListEntry}; If not in + * temp-allowlist, return null. + */ + @Nullable + TempFgsAllowListEntry getAllowedDurationAndReason(int uid) { final int index = mTempAllowListFgs.indexOfKey(uid); if (index < 0) { - return false; - } else if (mTempAllowListFgs.valueAt(index) < SystemClock.elapsedRealtime()) { + return null; + } else if (mTempAllowListFgs.valueAt(index).mExpirationTime + < SystemClock.elapsedRealtime()) { mTempAllowListFgs.removeAt(index); - return false; + return null; } else { - return true; + return mTempAllowListFgs.valueAt(index); } } diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 87cba54678c4..24953fc9c5d6 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; @@ -41,6 +42,7 @@ import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE; +import static android.os.PowerWhitelistManager.REASON_DENIED; import static android.os.Process.SCHED_OTHER; import static android.os.Process.THREAD_GROUP_BACKGROUND; import static android.os.Process.THREAD_GROUP_DEFAULT; @@ -51,7 +53,6 @@ import static android.os.Process.setProcessGroup; import static android.os.Process.setThreadPriority; import static android.os.Process.setThreadScheduler; -import static com.android.server.am.ActiveServices.FGS_FEATURE_DENIED; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU; @@ -1967,7 +1968,7 @@ public final class OomAdjuster { int clientProcState = cstate.getCurRawProcState(); // pass client's mAllowStartFgs to the app if client is not persistent process. - if (cstate.getAllowedStartFgs() != FGS_FEATURE_DENIED + if (cstate.getAllowedStartFgs() != REASON_DENIED && cstate.getMaxAdj() >= ProcessList.FOREGROUND_APP_ADJ) { state.setAllowStartFgs(cstate.getAllowedStartFgs()); } @@ -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; } diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java index 42172bf7e1df..534bd84a91a3 100644 --- a/services/core/java/com/android/server/am/PendingIntentController.java +++ b/services/core/java/com/android/server/am/PendingIntentController.java @@ -36,6 +36,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.PowerWhitelistManager; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.UserHandle; @@ -300,15 +301,16 @@ public class PendingIntentController { } } - void setPendingIntentWhitelistDuration(IIntentSender target, IBinder whitelistToken, - long duration, int type) { + void setPendingIntentAllowlistDuration(IIntentSender target, IBinder allowlistToken, + long duration, int type, @PowerWhitelistManager.ReasonCode int reasonCode, + @Nullable String reason) { if (!(target instanceof PendingIntentRecord)) { Slog.w(TAG, "markAsSentFromNotification(): not a PendingIntentRecord: " + target); return; } synchronized (mLock) { - ((PendingIntentRecord) target).setWhitelistDurationLocked(whitelistToken, duration, - type); + ((PendingIntentRecord) target).setAllowlistDurationLocked(allowlistToken, duration, + type, reasonCode, reason); } } diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index 0eb48f6da60d..51666acb8134 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -31,13 +31,14 @@ import android.content.Intent; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; +import android.os.PowerWhitelistManager; +import android.os.PowerWhitelistManager.ReasonCode; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.TransactionTooLargeException; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; -import android.util.Pair; import android.util.Slog; import android.util.TimeUtils; @@ -67,7 +68,7 @@ public final class PendingIntentRecord extends IIntentSender.Stub { * milliseconds, Integer is allowlist type defined at * {@link android.os.PowerWhitelistManager.TempAllowListType} */ - private ArrayMap<IBinder, Pair<Long, Integer>> mWhitelistDuration; + private ArrayMap<IBinder, TempAllowListDuration> mAllowlistDuration; private RemoteCallbackList<IResultReceiver> mCancelCallbacks; private ArraySet<IBinder> mAllowBgActivityStartsForActivitySender = new ArraySet<>(); private ArraySet<IBinder> mAllowBgActivityStartsForBroadcastSender = new ArraySet<>(); @@ -214,6 +215,21 @@ public final class PendingIntentRecord extends IIntentSender.Stub { } } + static final class TempAllowListDuration { + long duration; + int type; + @ReasonCode int reasonCode; + @Nullable String reason; + + TempAllowListDuration(long _duration, int _type, @ReasonCode int _reasonCode, + String _reason) { + duration = _duration; + type = _type; + reasonCode = _reasonCode; + reason = _reason; + } + } + PendingIntentRecord(PendingIntentController _controller, Key _k, int _u) { controller = _controller; key = _k; @@ -221,18 +237,19 @@ public final class PendingIntentRecord extends IIntentSender.Stub { ref = new WeakReference<>(this); } - void setWhitelistDurationLocked(IBinder whitelistToken, long duration, int type) { + void setAllowlistDurationLocked(IBinder allowlistToken, long duration, int type, + @ReasonCode int reasonCode, @Nullable String reason) { if (duration > 0) { - if (mWhitelistDuration == null) { - mWhitelistDuration = new ArrayMap<>(); + if (mAllowlistDuration == null) { + mAllowlistDuration = new ArrayMap<>(); } - mWhitelistDuration.put(whitelistToken, new Pair(duration, type)); - } else if (mWhitelistDuration != null) { - mWhitelistDuration.remove(whitelistToken); - if (mWhitelistDuration.size() <= 0) { - mWhitelistDuration = null; + mAllowlistDuration.put(allowlistToken, + new TempAllowListDuration(duration, type, reasonCode, reason)); + } else if (mAllowlistDuration != null) { + mAllowlistDuration.remove(allowlistToken); + if (mAllowlistDuration.size() <= 0) { + mAllowlistDuration = null; } - } this.stringName = null; } @@ -280,25 +297,25 @@ public final class PendingIntentRecord extends IIntentSender.Stub { return listeners; } - public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, + public void send(int code, Intent intent, String resolvedType, IBinder allowlistToken, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { - sendInner(code, intent, resolvedType, whitelistToken, finishedReceiver, + sendInner(code, intent, resolvedType, allowlistToken, finishedReceiver, requiredPermission, null, null, 0, 0, 0, options); } - public int sendWithResult(int code, Intent intent, String resolvedType, IBinder whitelistToken, + public int sendWithResult(int code, Intent intent, String resolvedType, IBinder allowlistToken, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { - return sendInner(code, intent, resolvedType, whitelistToken, finishedReceiver, + return sendInner(code, intent, resolvedType, allowlistToken, finishedReceiver, requiredPermission, null, null, 0, 0, 0, options); } - public int sendInner(int code, Intent intent, String resolvedType, IBinder whitelistToken, + public int sendInner(int code, Intent intent, String resolvedType, IBinder allowlistToken, IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo, String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle options) { if (intent != null) intent.setDefusable(true); if (options != null) options.setDefusable(true); - Pair<Long, Integer> duration = null; + TempAllowListDuration duration = null; Intent finalIntent = null; Intent[] allIntents = null; String[] allResolvedTypes = null; @@ -347,8 +364,8 @@ public final class PendingIntentRecord extends IIntentSender.Stub { mergedOptions.setCallerOptions(opts); } - if (mWhitelistDuration != null) { - duration = mWhitelistDuration.get(whitelistToken); + if (mAllowlistDuration != null) { + duration = mAllowlistDuration.get(allowlistToken); } if (key.type == ActivityManager.INTENT_SENDER_ACTIVITY @@ -377,7 +394,9 @@ public final class PendingIntentRecord extends IIntentSender.Stub { try { if (duration != null) { StringBuilder tag = new StringBuilder(64); - tag.append("pendingintent:"); + tag.append("setPendingIntentAllowlistDuration,reason:"); + tag.append(duration.reason == null ? "" : duration.reason); + tag.append(",pendingintent:"); UserHandle.formatUid(tag, callingUid); tag.append(":"); if (finalIntent.getAction() != null) { @@ -387,8 +406,8 @@ public final class PendingIntentRecord extends IIntentSender.Stub { } else if (finalIntent.getData() != null) { tag.append(finalIntent.getData().toSafeString()); } - controller.mAmInternal.tempWhitelistForPendingIntent(callingPid, callingUid, - uid, duration.first, duration.second, tag.toString()); + controller.mAmInternal.tempAllowlistForPendingIntent(callingPid, callingUid, + uid, duration.duration, duration.type, duration.reasonCode, tag.toString()); } boolean sendFinish = finishedReceiver != null; @@ -417,7 +436,8 @@ public final class PendingIntentRecord extends IIntentSender.Stub { allIntents, allResolvedTypes, resultTo, mergedOptions, userId, false /* validateIncomingUser */, this /* originatingPendingIntent */, - mAllowBgActivityStartsForActivitySender.contains(whitelistToken)); + mAllowBgActivityStartsForActivitySender.contains( + allowlistToken)); } else { res = controller.mAtmInternal.startActivityInPackage(uid, callingPid, callingUid, key.packageName, key.featureId, finalIntent, @@ -426,7 +446,7 @@ public final class PendingIntentRecord extends IIntentSender.Stub { false /* validateIncomingUser */, this /* originatingPendingIntent */, mAllowBgActivityStartsForActivitySender.contains( - whitelistToken)); + allowlistToken)); } } catch (RuntimeException e) { Slog.w(TAG, "Unable to send startActivity intent", e); @@ -439,8 +459,8 @@ public final class PendingIntentRecord extends IIntentSender.Stub { case ActivityManager.INTENT_SENDER_BROADCAST: try { final boolean allowedByToken = - mAllowBgActivityStartsForBroadcastSender.contains(whitelistToken); - final IBinder bgStartsToken = (allowedByToken) ? whitelistToken : null; + mAllowBgActivityStartsForBroadcastSender.contains(allowlistToken); + final IBinder bgStartsToken = (allowedByToken) ? allowlistToken : null; // If a completion callback has been requested, require // that the broadcast be delivered synchronously @@ -460,8 +480,8 @@ public final class PendingIntentRecord extends IIntentSender.Stub { case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE: try { final boolean allowedByToken = - mAllowBgActivityStartsForServiceSender.contains(whitelistToken); - final IBinder bgStartsToken = (allowedByToken) ? whitelistToken : null; + mAllowBgActivityStartsForServiceSender.contains(allowlistToken); + final IBinder bgStartsToken = (allowedByToken) ? allowlistToken : null; controller.mAmInternal.startServiceInPackage(uid, finalIntent, resolvedType, key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE, @@ -533,18 +553,23 @@ public final class PendingIntentRecord extends IIntentSender.Stub { pw.print(prefix); pw.print("sent="); pw.print(sent); pw.print(" canceled="); pw.println(canceled); } - if (mWhitelistDuration != null) { + if (mAllowlistDuration != null) { pw.print(prefix); - pw.print("whitelistDuration="); - for (int i = 0; i < mWhitelistDuration.size(); i++) { + pw.print("allowlistDuration="); + for (int i = 0; i < mAllowlistDuration.size(); i++) { if (i != 0) { pw.print(", "); } - pw.print(Integer.toHexString(System.identityHashCode(mWhitelistDuration.keyAt(i)))); + TempAllowListDuration entry = mAllowlistDuration.valueAt(i); + pw.print(Integer.toHexString(System.identityHashCode(mAllowlistDuration.keyAt(i)))); pw.print(":"); - TimeUtils.formatDuration(mWhitelistDuration.valueAt(i).first, pw); + TimeUtils.formatDuration(entry.duration, pw); pw.print("/"); - pw.print(mWhitelistDuration.valueAt(i).second); + pw.print(entry.type); + pw.print("/"); + pw.print(PowerWhitelistManager.reasonCodeToString(entry.reasonCode)); + pw.print("/"); + pw.print(entry.reason); } pw.println(); } @@ -572,18 +597,23 @@ public final class PendingIntentRecord extends IIntentSender.Stub { } sb.append(' '); sb.append(key.typeName()); - if (mWhitelistDuration != null) { - sb.append( " (whitelist: "); - for (int i = 0; i < mWhitelistDuration.size(); i++) { + if (mAllowlistDuration != null) { + sb.append(" (allowlist: "); + for (int i = 0; i < mAllowlistDuration.size(); i++) { if (i != 0) { sb.append(","); } + TempAllowListDuration entry = mAllowlistDuration.valueAt(i); sb.append(Integer.toHexString(System.identityHashCode( - mWhitelistDuration.keyAt(i)))); + mAllowlistDuration.keyAt(i)))); sb.append(":"); - TimeUtils.formatDuration(mWhitelistDuration.valueAt(i).first, sb); + TimeUtils.formatDuration(entry.duration, sb); + sb.append("/"); + sb.append(entry.type); + sb.append("/"); + sb.append(PowerWhitelistManager.reasonCodeToString(entry.reasonCode)); sb.append("/"); - sb.append(mWhitelistDuration.valueAt(i).second); + sb.append(entry.reason); } sb.append(")"); } diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java index 165312352990..3258f8af0da2 100644 --- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java @@ -22,6 +22,7 @@ import static com.android.server.am.ActivityManagerService.MY_PID; import static com.android.server.am.ProcessRecord.TAG; import android.app.ActivityManager; +import android.app.AnrController; import android.app.ApplicationErrorReport; import android.app.ApplicationExitInfo; import android.content.ComponentName; @@ -418,10 +419,16 @@ class ProcessErrorStateRecord { // Retrieve max ANR delay from AnrControllers without the mService lock since the // controllers might in turn call into apps - long anrDialogDelayMs = mService.mActivityTaskManager.getMaxAnrDelayMillis(aInfo); - if (aInfo != null && aInfo.packageName != null && anrDialogDelayMs > 0) { - Slog.i(TAG, "Delaying ANR dialog for " + aInfo.packageName + " for " + anrDialogDelayMs - + "ms"); + AnrController anrController = mService.mActivityTaskManager.getAnrController(aInfo); + long anrDialogDelayMs = 0; + if (anrController != null) { + String packageName = aInfo.packageName; + int uid = aInfo.uid; + anrDialogDelayMs = anrController.getAnrDelayMillis(packageName, uid); + // Might execute an async binder call to a system app to show an interim + // ANR progress UI + anrController.onAnrDelayStarted(packageName, uid); + Slog.i(TAG, "ANR delay of " + anrDialogDelayMs + "ms started for " + packageName); } synchronized (mService) { @@ -440,6 +447,7 @@ class ProcessErrorStateRecord { // Set the app's notResponding state, and look up the errorReportReceiver makeAppNotRespondingLSP(activityShortComponentName, annotation != null ? "ANR " + annotation : "ANR", info.toString()); + mDialogController.setAnrController(anrController); } // Notify package manager service to possibly update package state diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index cb07a06d8340..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); @@ -4395,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); @@ -4658,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) { @@ -4749,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/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java index 499fbcb0642d..6d783fc63901 100644 --- a/services/core/java/com/android/server/am/ProcessStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessStateRecord.java @@ -23,22 +23,23 @@ 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_NONEXISTENT; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.os.PowerWhitelistManager.REASON_BACKGROUND_ACTIVITY_PERMISSION; +import static android.os.PowerWhitelistManager.REASON_BACKGROUND_FGS_PERMISSION; +import static android.os.PowerWhitelistManager.REASON_COMPANION_DEVICE_MANAGER; +import static android.os.PowerWhitelistManager.REASON_DENIED; +import static android.os.PowerWhitelistManager.REASON_DEVICE_OWNER; +import static android.os.PowerWhitelistManager.REASON_PROFILE_OWNER; +import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALERT_WINDOW_PERMISSION; +import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALLOW_LISTED; +import static android.os.PowerWhitelistManager.REASON_SYSTEM_UID; +import static android.os.PowerWhitelistManager.ReasonCode; +import static android.os.PowerWhitelistManager.getReasonCodeFromProcState; +import static android.os.PowerWhitelistManager.reasonCodeToString; import static android.os.Process.NFC_UID; import static android.os.Process.ROOT_UID; import static android.os.Process.SHELL_UID; import static android.os.Process.SYSTEM_UID; -import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION; -import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION; -import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_COMPANION_APP; -import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST; -import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER; -import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_PROC_STATE; -import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER; -import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION; -import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_SYSTEM_UID; -import static com.android.server.am.ActiveServices.FGS_FEATURE_DENIED; -import static com.android.server.am.ActiveServices.fgsCodeToString; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ; import static com.android.server.am.ProcessRecord.TAG; @@ -329,7 +330,7 @@ final class ProcessStateRecord { * Does the process has permission to start FGS from background. */ @GuardedBy("mService") - private @ActiveServices.FgsFeatureRetCode int mAllowStartFgsByPermission; + private @ReasonCode int mAllowStartFgsByPermission = REASON_DENIED; /** * Can this process start FGS from background? @@ -337,7 +338,7 @@ final class ProcessStateRecord { * another process through service binding. */ @GuardedBy("mService") - private @ActiveServices.FgsFeatureRetCode int mAllowStartFgs; + private @ReasonCode int mAllowStartFgs = REASON_DENIED; /** * Debugging: primary thing impacting oom_adj. @@ -1152,44 +1153,47 @@ final class ProcessStateRecord { } @GuardedBy("mService") + int getAllowStartFgsState() { + return mAllowStartFgsState; + } + + @GuardedBy("mService") boolean isAllowedStartFgsState() { return mAllowStartFgsState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE; } @GuardedBy("mService") void setAllowStartFgsByPermission() { - int ret = FGS_FEATURE_DENIED; - if (ret == FGS_FEATURE_DENIED) { - boolean isSystem = false; - final int uid = UserHandle.getAppId(mApp.info.uid); - switch (uid) { - case ROOT_UID: - case SYSTEM_UID: - case NFC_UID: - case SHELL_UID: - isSystem = true; - break; - default: - isSystem = false; - break; - } + int ret = REASON_DENIED; + boolean isSystem = false; + final int uid = UserHandle.getAppId(mApp.info.uid); + switch (uid) { + case ROOT_UID: + case SYSTEM_UID: + case NFC_UID: + case SHELL_UID: + isSystem = true; + break; + default: + isSystem = false; + break; + } - if (isSystem) { - ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_UID; - } + if (isSystem) { + ret = REASON_SYSTEM_UID; } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { if (ActivityManager.checkComponentPermission(START_ACTIVITIES_FROM_BACKGROUND, mApp.info.uid, -1, true) == PERMISSION_GRANTED) { - ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION; + ret = REASON_BACKGROUND_ACTIVITY_PERMISSION; } else if (ActivityManager.checkComponentPermission( START_FOREGROUND_SERVICES_FROM_BACKGROUND, mApp.info.uid, -1, true) == PERMISSION_GRANTED) { - ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION; + ret = REASON_BACKGROUND_FGS_PERMISSION; } else if (ActivityManager.checkComponentPermission(SYSTEM_ALERT_WINDOW, mApp.info.uid, -1, true) == PERMISSION_GRANTED) { - ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION; + ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION; } } mAllowStartFgs = mAllowStartFgsByPermission = ret; @@ -1197,59 +1201,65 @@ final class ProcessStateRecord { @GuardedBy("mService") void setAllowStartFgs() { - if (mAllowStartFgs != FGS_FEATURE_DENIED) { + if (mAllowStartFgs != REASON_DENIED) { return; } - if (mAllowStartFgs == FGS_FEATURE_DENIED) { + if (mAllowStartFgs == REASON_DENIED) { if (isAllowedStartFgsState()) { - mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_PROC_STATE; + mAllowStartFgs = getReasonCodeFromProcState(mAllowStartFgsState); } } - if (mAllowStartFgs == FGS_FEATURE_DENIED) { + if (mAllowStartFgs == REASON_DENIED) { // Is the calling UID a device owner app? if (mService.mInternal != null) { if (mService.mInternal.isDeviceOwner(mApp.info.uid)) { - mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER; + mAllowStartFgs = REASON_DEVICE_OWNER; } } } - if (mAllowStartFgs == FGS_FEATURE_DENIED) { + if (mAllowStartFgs == REASON_DENIED) { if (mService.mInternal != null) { final boolean isCompanionApp = mService.mInternal.isAssociatedCompanionApp( UserHandle.getUserId(mApp.info.uid), mApp.info.uid); if (isCompanionApp) { - mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_COMPANION_APP; + mAllowStartFgs = REASON_COMPANION_DEVICE_MANAGER; } } } - if (mAllowStartFgs == FGS_FEATURE_DENIED) { + if (mAllowStartFgs == REASON_DENIED) { // Is the calling UID a profile owner app? if (mService.mInternal != null) { if (mService.mInternal.isProfileOwner(mApp.info.uid)) { - mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER; + mAllowStartFgs = REASON_PROFILE_OWNER; } } } - if (mAllowStartFgs == FGS_FEATURE_DENIED) { + if (mAllowStartFgs == REASON_DENIED) { // uid is on DeviceIdleController's user/system allowlist // or AMS's FgsStartTempAllowList. - if (mService.isAllowlistedForFgsStartLOSP(mApp.info.uid)) { - mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST; + FgsStartTempAllowList.TempFgsAllowListEntry entry = + mService.isAllowlistedForFgsStartLOSP(mApp.info.uid); + if (entry != null) { + if (entry == ActivityManagerService.FAKE_TEMP_ALLOWLIST_ENTRY) { + mAllowStartFgs = REASON_SYSTEM_ALLOW_LISTED; + } else { + mAllowStartFgs = entry.mReasonCode; + } } } } @GuardedBy("mService") - void setAllowStartFgs(@ActiveServices.FgsFeatureRetCode int allowStartFgs) { + void setAllowStartFgs(@ReasonCode int allowStartFgs) { mAllowStartFgs = allowStartFgs; } @GuardedBy("mService") - @ActiveServices.FgsFeatureRetCode int getAllowedStartFgs() { + @ReasonCode int getAllowedStartFgs() { return mAllowStartFgs; } @@ -1291,9 +1301,9 @@ final class ProcessStateRecord { pw.println(); pw.print(prefix); pw.print("allowStartFgsState="); pw.println(mAllowStartFgsState); - if (mAllowStartFgs != FGS_FEATURE_DENIED) { + if (mAllowStartFgs != REASON_DENIED) { pw.print(prefix); pw.print("allowStartFgs="); - pw.println(fgsCodeToString(mAllowStartFgs)); + pw.println(reasonCodeToString(mAllowStartFgs)); } if (mHasShownUi || mApp.mProfile.hasPendingUiClean()) { pw.print(prefix); pw.print("hasShownUi="); pw.print(mHasShownUi); diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 485087d29422..3ab95d131fad 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -18,6 +18,7 @@ package com.android.server.am; import static android.app.PendingIntent.FLAG_IMMUTABLE; import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; +import static android.os.PowerWhitelistManager.REASON_DENIED; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -36,6 +37,7 @@ import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.IBinder; +import android.os.PowerWhitelistManager; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; @@ -101,7 +103,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN ProcessRecord isolatedProc; // keep track of isolated process, if requested ServiceState tracker; // tracking service execution, may be null ServiceState restartTracker; // tracking service restart - boolean whitelistManager; // any bindings to this service have BIND_ALLOW_WHITELIST_MANAGEMENT? + boolean allowlistManager; // any bindings to this service have BIND_ALLOW_WHITELIST_MANAGEMENT? boolean delayed; // are we waiting to start this service in the background? boolean fgRequired; // is the service required to go foreground after starting? boolean fgWaiting; // is a timeout for going foreground already scheduled? @@ -156,11 +158,14 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN // the most recent package that start/bind this service. String mRecentCallingPackage; + // the most recent uid that start/bind this service. + int mRecentCallingUid; // allow the service becomes foreground service? Service started from background may not be // allowed to become a foreground service. - @ActiveServices.FgsFeatureRetCode int mAllowStartForeground; + @PowerWhitelistManager.ReasonCode int mAllowStartForeground = REASON_DENIED; String mInfoAllowStartForeground; + FgsStartTempAllowList.TempFgsAllowListEntry mInfoTempFgsAllowListReason; boolean mLoggedInfoAllowStartForeground; String stringName; // caching of toString @@ -309,7 +314,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN if (isolatedProc != null) { isolatedProc.dumpDebug(proto, ServiceRecordProto.ISOLATED_PROC); } - proto.write(ServiceRecordProto.WHITELIST_MANAGER, whitelistManager); + proto.write(ServiceRecordProto.WHITELIST_MANAGER, allowlistManager); proto.write(ServiceRecordProto.DELAYED, delayed); if (isForeground || foregroundId != 0) { long fgToken = proto.start(ServiceRecordProto.FOREGROUND); @@ -410,8 +415,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN if (isolatedProc != null) { pw.print(prefix); pw.print("isolatedProc="); pw.println(isolatedProc); } - if (whitelistManager) { - pw.print(prefix); pw.print("whitelistManager="); pw.println(whitelistManager); + if (allowlistManager) { + pw.print(prefix); pw.print("allowlistManager="); pw.println(allowlistManager); } if (mIsAllowedBgActivityStartsByBinding) { pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByBinding="); @@ -429,6 +434,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN pw.println(mAllowWhileInUsePermissionInFgs); pw.print(prefix); pw.print("recentCallingPackage="); pw.println(mRecentCallingPackage); + pw.print(prefix); pw.print("recentCallingUid="); + pw.println(mRecentCallingUid); pw.print(prefix); pw.print("allowStartForeground="); pw.println(mAllowStartForeground); pw.print(prefix); pw.print("infoAllowStartForeground="); @@ -894,13 +901,13 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN return false; } - public void updateWhitelistManager() { - whitelistManager = false; + public void updateAllowlistManager() { + allowlistManager = false; for (int conni=connections.size()-1; conni>=0; conni--) { ArrayList<ConnectionRecord> cr = connections.valueAt(conni); for (int i=0; i<cr.size(); i++) { if ((cr.get(i).flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) { - whitelistManager = true; + allowlistManager = true; return; } } diff --git a/services/core/java/com/android/server/am/StrictModeViolationDialog.java b/services/core/java/com/android/server/am/StrictModeViolationDialog.java index 4cc1fc1b7d09..9dddd658575b 100644 --- a/services/core/java/com/android/server/am/StrictModeViolationDialog.java +++ b/services/core/java/com/android/server/am/StrictModeViolationDialog.java @@ -82,6 +82,11 @@ final class StrictModeViolationDialog extends BaseErrorDialog { DISMISS_TIMEOUT); } + @Override + protected void closeDialog() { + mHandler.obtainMessage(ACTION_OK).sendToTarget(); + } + private final Handler mHandler = new Handler() { public void handleMessage(Message msg) { synchronized (mService.mProcLock) { diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index a776458d63c5..44dcc205a9d0 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -339,7 +339,10 @@ public class AppOpsService extends IAppOpsService.Stub { SparseIntArray mProfileOwners; @GuardedBy("this") - private CheckOpsDelegate mCheckOpsDelegate; + private CheckOpsDelegate mAppOpsPolicy; + + @GuardedBy("this") + private CheckOpsDelegateDispatcher mCheckOpsDelegateDispatcher; /** * Reverse lookup for {@link AppOpsManager#opToSwitch(int)}. Initialized once and never @@ -1770,6 +1773,17 @@ public class AppOpsService extends IAppOpsService.Stub { mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); } + /** + * Sets a policy for handling app ops. + * + * @param appOpsPolicy The policy. + */ + public void setAppOpsPolicy(@Nullable CheckOpsDelegate appOpsPolicy) { + synchronized (AppOpsService.this) { + mAppOpsPolicy = appOpsPolicy; + } + } + public void packageRemoved(int uid, String packageName) { synchronized (this) { UidState uidState = mUidStates.get(uid); @@ -2868,13 +2882,19 @@ public class AppOpsService extends IAppOpsService.Stub { public CheckOpsDelegate getAppOpsServiceDelegate() { synchronized (this) { - return mCheckOpsDelegate; + return (mCheckOpsDelegateDispatcher != null) + ? mCheckOpsDelegateDispatcher.getCheckOpsDelegate() + : null; } } public void setAppOpsServiceDelegate(CheckOpsDelegate delegate) { synchronized (this) { - mCheckOpsDelegate = delegate; + if (delegate != null) { + mCheckOpsDelegateDispatcher = new CheckOpsDelegateDispatcher(delegate); + } else { + mCheckOpsDelegateDispatcher = null; + } } } @@ -2889,19 +2909,28 @@ public class AppOpsService extends IAppOpsService.Stub { } private int checkOperationInternal(int code, int uid, String packageName, boolean raw) { - final CheckOpsDelegate delegate; - synchronized (this) { - delegate = mCheckOpsDelegate; + final CheckOpsDelegate policy; + final CheckOpsDelegateDispatcher delegateDispatcher; + synchronized (AppOpsService.this) { + policy = mAppOpsPolicy; + delegateDispatcher = mCheckOpsDelegateDispatcher; } - if (delegate == null) { - return checkOperationImpl(code, uid, packageName, raw); + if (policy != null) { + if (delegateDispatcher != null) { + return policy.checkOperation(code, uid, packageName, raw, + delegateDispatcher::checkOperationImpl); + } else { + return policy.checkOperation(code, uid, packageName, raw, + AppOpsService.this::checkOperationImpl); + } + } else if (delegateDispatcher != null) { + delegateDispatcher.getCheckOpsDelegate().checkOperation(code, uid, + packageName, raw, AppOpsService.this::checkOperationImpl); } - return delegate.checkOperation(code, uid, packageName, raw, - AppOpsService.this::checkOperationImpl); + return checkOperationImpl(code, uid, packageName, raw); } - private int checkOperationImpl(int code, int uid, String packageName, - boolean raw) { + private int checkOperationImpl(int code, int uid, String packageName, boolean raw) { verifyIncomingOp(code); verifyIncomingPackage(packageName, UserHandle.getUserId(uid)); @@ -2956,15 +2985,25 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public int checkAudioOperation(int code, int usage, int uid, String packageName) { - final CheckOpsDelegate delegate; - synchronized (this) { - delegate = mCheckOpsDelegate; + final CheckOpsDelegate policy; + final CheckOpsDelegateDispatcher delegateDispatcher; + synchronized (AppOpsService.this) { + policy = mAppOpsPolicy; + delegateDispatcher = mCheckOpsDelegateDispatcher; } - if (delegate == null) { - return checkAudioOperationImpl(code, usage, uid, packageName); + if (policy != null) { + if (delegateDispatcher != null) { + return policy.checkAudioOperation(code, usage, uid, packageName, + delegateDispatcher::checkAudioOperationImpl); + } else { + return policy.checkAudioOperation(code, usage, uid, packageName, + AppOpsService.this::checkAudioOperationImpl); + } + } else if (delegateDispatcher != null) { + delegateDispatcher.getCheckOpsDelegate().checkAudioOperation(code, usage, + uid, packageName, AppOpsService.this::checkAudioOperationImpl); } - return delegate.checkAudioOperation(code, usage, uid, packageName, - AppOpsService.this::checkAudioOperationImpl); + return checkAudioOperationImpl(code, usage, uid, packageName); } private int checkAudioOperationImpl(int code, int usage, int uid, String packageName) { @@ -3078,17 +3117,29 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public int noteOperation(int code, int uid, String packageName, String attributionTag, boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage) { - final CheckOpsDelegate delegate; - synchronized (this) { - delegate = mCheckOpsDelegate; - } - if (delegate == null) { - return noteOperationImpl(code, uid, packageName, attributionTag, - shouldCollectAsyncNotedOp, message, shouldCollectMessage); + final CheckOpsDelegate policy; + final CheckOpsDelegateDispatcher delegateDispatcher; + synchronized (AppOpsService.this) { + policy = mAppOpsPolicy; + delegateDispatcher = mCheckOpsDelegateDispatcher; + } + if (policy != null) { + if (delegateDispatcher != null) { + return policy.noteOperation(code, uid, packageName, attributionTag, + shouldCollectAsyncNotedOp, message, shouldCollectMessage, + delegateDispatcher::noteOperationImpl); + } else { + return policy.noteOperation(code, uid, packageName, attributionTag, + shouldCollectAsyncNotedOp, message, shouldCollectMessage, + AppOpsService.this::noteOperationImpl); + } + } else if (delegateDispatcher != null) { + delegateDispatcher.getCheckOpsDelegate().noteOperation(code, uid, packageName, + attributionTag, shouldCollectAsyncNotedOp, message, shouldCollectMessage, + AppOpsService.this::noteOperationImpl); } - return delegate.noteOperation(code, uid, packageName, attributionTag, - shouldCollectAsyncNotedOp, message, shouldCollectMessage, - AppOpsService.this::noteOperationImpl); + return noteOperationImpl(code, uid, packageName, attributionTag, + shouldCollectAsyncNotedOp, message, shouldCollectMessage); } private int noteOperationImpl(int code, int uid, @Nullable String packageName, @@ -6589,7 +6640,6 @@ public class AppOpsService extends IAppOpsService.Stub { } } - /** * Async task for writing note op stack trace, op code, package name and version to file * More specifically, writes all the collected ops from {@link #mNoteOpCallerStacktraces} @@ -6726,4 +6776,34 @@ public class AppOpsService extends IAppOpsService.Stub { } } } + + private final class CheckOpsDelegateDispatcher { + private final @NonNull CheckOpsDelegate mCheckOpsDelegate; + + CheckOpsDelegateDispatcher(@NonNull CheckOpsDelegate checkOpsDelegate) { + mCheckOpsDelegate = checkOpsDelegate; + } + + public @NonNull CheckOpsDelegate getCheckOpsDelegate() { + return mCheckOpsDelegate; + } + + public int checkOperationImpl(int code, int uid, String packageName, boolean raw) { + return mCheckOpsDelegate.checkOperation(code, uid, packageName, raw, + AppOpsService.this::checkOperationImpl); + } + + public int checkAudioOperationImpl(int code, int usage, int uid, String packageName) { + return mCheckOpsDelegate.checkAudioOperation(code, usage, uid, packageName, + AppOpsService.this::checkAudioOperationImpl); + } + + public int noteOperationImpl(int code, int uid, @Nullable String packageName, + @Nullable String featureId, boolean shouldCollectAsyncNotedOp, + @Nullable String message, boolean shouldCollectMessage) { + return mCheckOpsDelegate.noteOperation(code, uid, packageName, featureId, + shouldCollectAsyncNotedOp, message, shouldCollectMessage, + AppOpsService.this::noteOperationImpl); + } + } } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index f5b94177a2d9..8363c9d203d5 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -6958,7 +6958,10 @@ public class AudioService extends IAudioService.Stub private void onSetVolumeIndexOnDevice(@NonNull DeviceVolumeUpdate update) { final VolumeStreamState streamState = mStreamStates[update.mStreamType]; if (update.hasVolumeIndex()) { - final int index = update.getVolumeIndex(); + int index = update.getVolumeIndex(); + if (!checkSafeMediaVolume(update.mStreamType, index, update.mDevice)) { + index = safeMediaVolumeIndex(update.mDevice); + } streamState.setIndex(index, update.mDevice, update.mCaller, // trusted as index is always validated before message is posted true /*hasModifyAudioSettings*/); diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java index 507600783aa4..322c210f6e5b 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardService.java +++ b/services/core/java/com/android/server/clipboard/ClipboardService.java @@ -61,7 +61,6 @@ import android.widget.Toast; import com.android.server.LocalServices; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; import com.android.server.UiThread; import com.android.server.contentcapture.ContentCaptureManagerInternal; import com.android.server.uri.UriGrantsManagerInternal; @@ -207,7 +206,7 @@ public class ClipboardService extends SystemService { new ClipData.Item(contents)); synchronized(mClipboards) { setPrimaryClipInternal(getClipboard(0), clip, - android.os.Process.SYSTEM_UID); + android.os.Process.SYSTEM_UID, null); } } }); @@ -247,6 +246,8 @@ public class ClipboardService extends SystemService { ClipData primaryClip; /** UID that set {@link #primaryClip}. */ int primaryClipUid = android.os.Process.NOBODY_UID; + /** Application label of the app that set {@link #primaryClip}. */ + CharSequence mPrimaryClipAppLabel; final HashSet<String> activePermissionOwners = new HashSet<String>(); @@ -365,7 +366,7 @@ public class ClipboardService extends SystemService { return; } checkDataOwnerLocked(clip, intendingUid); - setPrimaryClipInternal(clip, intendingUid); + setPrimaryClipInternal(clip, intendingUid, callingPackage); } } @@ -378,7 +379,7 @@ public class ClipboardService extends SystemService { intendingUid, intendingUserId)) { return; } - setPrimaryClipInternal(null, intendingUid); + setPrimaryClipInternal(null, intendingUid, callingPackage); } } @@ -509,6 +510,11 @@ public class ClipboardService extends SystemService { } void setPrimaryClipInternal(@Nullable ClipData clip, int uid) { + setPrimaryClipInternal(clip, uid, null); + } + + private void setPrimaryClipInternal( + @Nullable ClipData clip, int uid, @Nullable String sourcePackage) { // Push clipboard to host, if any if (mHostClipboardMonitor != null) { if (clip == null) { @@ -522,9 +528,20 @@ public class ClipboardService extends SystemService { } } + // Retrieve the app label of the source of the clip data + CharSequence sourceAppLabel = null; + if (clip != null && sourcePackage != null) { + try { + sourceAppLabel = + mPm.getApplicationLabel(mPm.getApplicationInfo(sourcePackage, 0)); + } catch (PackageManager.NameNotFoundException e) { + // leave label as null + } + } + // Update this user final int userId = UserHandle.getUserId(uid); - setPrimaryClipInternal(getClipboard(userId), clip, uid); + setPrimaryClipInternal(getClipboard(userId), clip, uid, sourceAppLabel); // Update related users List<UserInfo> related = getRelatedProfiles(userId); @@ -558,7 +575,8 @@ public class ClipboardService extends SystemService { final boolean canCopyIntoProfile = !hasRestriction( UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id); if (canCopyIntoProfile) { - setPrimaryClipInternal(getClipboard(id), clip, uid); + setPrimaryClipInternal( + getClipboard(id), clip, uid, sourceAppLabel); } } } @@ -568,6 +586,11 @@ public class ClipboardService extends SystemService { void setPrimaryClipInternal(PerUserClipboard clipboard, @Nullable ClipData clip, int uid) { + setPrimaryClipInternal(clipboard, clip, uid, null); + } + + private void setPrimaryClipInternal(PerUserClipboard clipboard, @Nullable ClipData clip, + int uid, @Nullable CharSequence sourceAppLabel) { revokeUris(clipboard); clipboard.activePermissionOwners.clear(); if (clip == null && clipboard.primaryClip == null) { @@ -576,8 +599,10 @@ public class ClipboardService extends SystemService { clipboard.primaryClip = clip; if (clip != null) { clipboard.primaryClipUid = uid; + clipboard.mPrimaryClipAppLabel = sourceAppLabel; } else { clipboard.primaryClipUid = android.os.Process.NOBODY_UID; + clipboard.mPrimaryClipAppLabel = null; } if (clip != null) { final ClipDescription description = clip.getDescription(); @@ -861,29 +886,23 @@ public class ClipboardService extends SystemService { && mAutofillInternal.isAugmentedAutofillServiceForUser(uid, userId)) { return; } - // Load the labels for the calling app and the app that set the clipboard content. - final long ident = Binder.clearCallingIdentity(); + try { - final IPackageManager pm = AppGlobals.getPackageManager(); + CharSequence callingAppLabel = mPm.getApplicationLabel( + mPm.getApplicationInfo(callingPackage, 0)); String message; - final CharSequence callingLabel = mPm.getApplicationLabel( - pm.getApplicationInfo(callingPackage, 0, userId)); - final String[] packagesForUid = pm.getPackagesForUid(clipboard.primaryClipUid); - if (packagesForUid != null && packagesForUid.length > 0) { - final CharSequence clipLabel = mPm.getApplicationLabel( - pm.getApplicationInfo(packagesForUid[0], 0, - UserHandle.getUserId(clipboard.primaryClipUid))); - message = callingLabel + " pasted from " + clipLabel; + if (clipboard.mPrimaryClipAppLabel != null) { + message = callingAppLabel + " pasted from " + clipboard.mPrimaryClipAppLabel; } else { - message = callingLabel + " pasted from clipboard"; + message = callingAppLabel + " pasted from clipboard"; } Slog.i(TAG, message); - Toast.makeText(getContext(), UiThread.get().getLooper(), message, Toast.LENGTH_SHORT) - .show(); - } catch (RemoteException e) { - /* ignore */ - } finally { - Binder.restoreCallingIdentity(ident); + Binder.withCleanCallingIdentity(() -> + Toast.makeText(getContext(), UiThread.get().getLooper(), message, + Toast.LENGTH_SHORT) + .show()); + } catch (PackageManager.NameNotFoundException e) { + // do nothing } } } diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java index a62f67a743ad..30cbf2745638 100644 --- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java +++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java @@ -61,7 +61,10 @@ public abstract class BrightnessMappingStrategy { private static final Plog PLOG = Plog.createSystemPlog(TAG); @Nullable - public static BrightnessMappingStrategy create(Resources resources) { + public static BrightnessMappingStrategy create(Resources resources, + DisplayDeviceConfig displayDeviceConfig) { + + // Display independent values float[] luxLevels = getLuxLevels(resources.getIntArray( com.android.internal.R.array.config_autoBrightnessLevels)); int[] brightnessLevelsBacklight = resources.getIntArray( @@ -71,32 +74,22 @@ public abstract class BrightnessMappingStrategy { float autoBrightnessAdjustmentMaxGamma = resources.getFraction( com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma, 1, 1); - - float[] nitsRange = getFloatArray(resources.obtainTypedArray( - com.android.internal.R.array.config_screenBrightnessNits)); - int[] backlightRange = resources.getIntArray( - com.android.internal.R.array.config_screenBrightnessBacklight); - long shortTermModelTimeout = resources.getInteger( com.android.internal.R.integer.config_autoBrightnessShortTermModelTimeout); - if (isValidMapping(nitsRange, backlightRange) + // Display dependent values - used for physical mapping strategy nits -> brightness + final float[] nitsRange = displayDeviceConfig.getNits(); + final float[] brightnessRange = displayDeviceConfig.getBrightness(); + + if (isValidMapping(nitsRange, brightnessRange) && isValidMapping(luxLevels, brightnessLevelsNits)) { - int minimumBacklight = resources.getInteger( - com.android.internal.R.integer.config_screenBrightnessSettingMinimum); - int maximumBacklight = resources.getInteger( - com.android.internal.R.integer.config_screenBrightnessSettingMaximum); - if (backlightRange[0] > minimumBacklight - || backlightRange[backlightRange.length - 1] < maximumBacklight) { - Slog.w(TAG, "Screen brightness mapping does not cover whole range of available " + - "backlight values, autobrightness functionality may be impaired."); - } + BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder( luxLevels, brightnessLevelsNits); builder.setShortTermModelTimeoutMillis(shortTermModelTimeout); builder.setShortTermModelLowerLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO); builder.setShortTermModelUpperLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO); - return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange, + return new PhysicalMappingStrategy(builder.build(), nitsRange, brightnessRange, autoBrightnessAdjustmentMaxGamma); } else if (isValidMapping(luxLevels, brightnessLevelsBacklight)) { return new SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight, @@ -264,11 +257,11 @@ public abstract class BrightnessMappingStrategy { public abstract boolean setAutoBrightnessAdjustment(float adjustment); /** - * Converts the provided backlight value to nits if possible. + * Converts the provided brightness value to nits if possible. * - * Returns -1.0f if there's no available mapping for the backlight to nits. + * Returns -1.0f if there's no available mapping for the brightness to nits. */ - public abstract float convertToNits(int backlight); + public abstract float convertToNits(float brightness); /** * Adds a user interaction data point to the brightness mapping. @@ -603,7 +596,7 @@ public abstract class BrightnessMappingStrategy { } @Override - public float convertToNits(int backlight) { + public float convertToNits(float brightness) { return -1.0f; } @@ -701,37 +694,39 @@ public abstract class BrightnessMappingStrategy { // in nits. private Spline mBrightnessSpline; - // A spline mapping from nits to the corresponding backlight value, normalized to the range + // A spline mapping from nits to the corresponding brightness value, normalized to the range // [0, 1.0]. - private Spline mNitsToBacklightSpline; + private Spline mNitsToBrightnessSpline; + + // A spline mapping from the system brightness value, normalized to the range [0, 1.0], to + // a brightness in nits. + private Spline mBrightnessToNitsSpline; // The default brightness configuration. private final BrightnessConfiguration mDefaultConfig; - // A spline mapping from the device's backlight value, normalized to the range [0, 1.0], to - // a brightness in nits. - private Spline mBacklightToNitsSpline; - - private float[] mNits; - private int[] mBacklight; + private final float[] mNits; + private final float[] mBrightness; private boolean mBrightnessRangeAdjustmentApplied; - private float mMaxGamma; + private final float mMaxGamma; private float mAutoBrightnessAdjustment; private float mUserLux; private float mUserBrightness; public PhysicalMappingStrategy(BrightnessConfiguration config, float[] nits, - int[] backlight, float maxGamma) { - Preconditions.checkArgument(nits.length != 0 && backlight.length != 0, - "Nits and backlight arrays must not be empty!"); - Preconditions.checkArgument(nits.length == backlight.length, - "Nits and backlight arrays must be the same length!"); + float[] brightness, float maxGamma) { + + Preconditions.checkArgument(nits.length != 0 && brightness.length != 0, + "Nits and brightness arrays must not be empty!"); + + Preconditions.checkArgument(nits.length == brightness.length, + "Nits and brightness arrays must be the same length!"); Objects.requireNonNull(config); Preconditions.checkArrayElementsInRange(nits, 0, Float.MAX_VALUE, "nits"); - Preconditions.checkArrayElementsInRange(backlight, - PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON, "backlight"); + Preconditions.checkArrayElementsInRange(brightness, + PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, "brightness"); mMaxGamma = maxGamma; mAutoBrightnessAdjustment = 0; @@ -739,7 +734,7 @@ public abstract class BrightnessMappingStrategy { mUserBrightness = -1; mNits = nits; - mBacklight = backlight; + mBrightness = brightness; computeNitsBrightnessSplines(mNits); mDefaultConfig = config; @@ -784,15 +779,15 @@ public abstract class BrightnessMappingStrategy { public float getBrightness(float lux, String packageName, @ApplicationInfo.Category int category) { float nits = mBrightnessSpline.interpolate(lux); - float backlight = mNitsToBacklightSpline.interpolate(nits); + float brightness = mNitsToBrightnessSpline.interpolate(nits); // Correct the brightness according to the current application and its category, but - // only if no user data point is set (as this will oevrride the user setting). + // only if no user data point is set (as this will override the user setting). if (mUserLux == -1) { - backlight = correctBrightness(backlight, packageName, category); + brightness = correctBrightness(brightness, packageName, category); } else if (mLoggingEnabled) { Slog.d(TAG, "user point set, correction not applied"); } - return backlight; + return brightness; } @Override @@ -817,8 +812,8 @@ public abstract class BrightnessMappingStrategy { } @Override - public float convertToNits(int backlight) { - return mBacklightToNitsSpline.interpolate(normalizeAbsoluteBrightness(backlight)); + public float convertToNits(float brightness) { + return mBrightnessToNitsSpline.interpolate(brightness); } @Override @@ -884,7 +879,8 @@ public abstract class BrightnessMappingStrategy { pw.println("PhysicalMappingStrategy"); pw.println(" mConfig=" + mConfig); pw.println(" mBrightnessSpline=" + mBrightnessSpline); - pw.println(" mNitsToBacklightSpline=" + mNitsToBacklightSpline); + pw.println(" mNitsToBrightnessSpline=" + mNitsToBrightnessSpline); + pw.println(" mBrightnessToNitsSpline=" + mBrightnessToNitsSpline); pw.println(" mMaxGamma=" + mMaxGamma); pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment); pw.println(" mUserLux=" + mUserLux); @@ -894,31 +890,25 @@ public abstract class BrightnessMappingStrategy { } private void computeNitsBrightnessSplines(float[] nits) { - final int len = nits.length; - float[] normalizedBacklight = new float[len]; - for (int i = 0; i < len; i++) { - normalizedBacklight[i] = normalizeAbsoluteBrightness(mBacklight[i]); - } - - mNitsToBacklightSpline = Spline.createSpline(nits, normalizedBacklight); - mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits); + mNitsToBrightnessSpline = Spline.createSpline(nits, mBrightness); + mBrightnessToNitsSpline = Spline.createSpline(mBrightness, nits); } private void computeSpline() { Pair<float[], float[]> defaultCurve = mConfig.getCurve(); float[] defaultLux = defaultCurve.first; float[] defaultNits = defaultCurve.second; - float[] defaultBacklight = new float[defaultNits.length]; - for (int i = 0; i < defaultBacklight.length; i++) { - defaultBacklight[i] = mNitsToBacklightSpline.interpolate(defaultNits[i]); + float[] defaultBrightness = new float[defaultNits.length]; + for (int i = 0; i < defaultBrightness.length; i++) { + defaultBrightness[i] = mNitsToBrightnessSpline.interpolate(defaultNits[i]); } - Pair<float[], float[]> curve = getAdjustedCurve(defaultLux, defaultBacklight, mUserLux, + Pair<float[], float[]> curve = getAdjustedCurve(defaultLux, defaultBrightness, mUserLux, mUserBrightness, mAutoBrightnessAdjustment, mMaxGamma); float[] lux = curve.first; - float[] backlight = curve.second; - float[] nits = new float[backlight.length]; + float[] brightness = curve.second; + float[] nits = new float[brightness.length]; for (int i = 0; i < nits.length; i++) { - nits[i] = mBacklightToNitsSpline.interpolate(backlight[i]); + nits[i] = mBrightnessToNitsSpline.interpolate(brightness[i]); } mBrightnessSpline = Spline.createSpline(lux, nits); } @@ -926,7 +916,7 @@ public abstract class BrightnessMappingStrategy { private float getUnadjustedBrightness(float lux) { Pair<float[], float[]> curve = mConfig.getCurve(); Spline spline = Spline.createSpline(curve.first, curve.second); - return mNitsToBacklightSpline.interpolate(spline.interpolate(lux)); + return mNitsToBrightnessSpline.interpolate(spline.interpolate(lux)); } private float correctBrightness(float brightness, String packageName, int category) { diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 49328f1019c3..0071b2f558c4 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -18,9 +18,12 @@ package com.android.server.display; import android.annotation.NonNull; import android.content.Context; +import android.content.res.Resources; import android.os.Environment; import android.os.PowerManager; +import android.util.MathUtils; import android.util.Slog; +import android.util.Spline; import android.view.DisplayAddress; import com.android.internal.R; @@ -72,15 +75,31 @@ public class DisplayDeviceConfig { private final Context mContext; + // Nits and backlight values that are loaded from either the display device config file, or + // config.xml. These are the raw values and just used for the dumpsys + private float[] mRawNits; + private float[] mRawBacklight; + + // These arrays are calculated from the raw arrays, but clamped to contain values equal to and + // between mBacklightMinimum and mBacklightMaximum. These three arrays should all be the same + // length + // Nits array that is used to store the entire range of nits values that the device supports private float[] mNits; + // Backlight array holds the values that the HAL uses to display the corresponding nits values + private float[] mBacklight; + // Purely an array that covers the ranges of values 0.0 - 1.0, indicating the system brightness + // for the corresponding values above private float[] mBrightness; - private float mBrightnessMinimum = Float.NaN; - private float mBrightnessMaximum = Float.NaN; + + private float mBacklightMinimum = Float.NaN; + private float mBacklightMaximum = Float.NaN; private float mBrightnessDefault = Float.NaN; private float mBrightnessRampFastDecrease = Float.NaN; private float mBrightnessRampFastIncrease = Float.NaN; private float mBrightnessRampSlowDecrease = Float.NaN; private float mBrightnessRampSlowIncrease = Float.NaN; + private Spline mBrightnessToBacklightSpline; + private Spline mBacklightToBrightnessSpline; private List<String> mQuirks; private boolean mIsHighBrightnessModeEnabled = false; private HighBrightnessModeData mHbmData; @@ -167,7 +186,7 @@ public class DisplayDeviceConfig { } /** - * Return the brightness mapping nits array if one is defined in the configuration file. + * Return the brightness mapping nits array. * * @return The brightness mapping nits array. */ @@ -176,22 +195,40 @@ public class DisplayDeviceConfig { } /** - * Return the brightness mapping value array if one is defined in the configuration file. + * Return the brightness mapping backlight array. * - * @return The brightness mapping value array. + * @return The backlight mapping value array. */ - public float[] getBrightness() { - return mBrightness; + public float[] getBacklight() { + return mBacklight; } - public float getBrightnessMinimum() { - return mBrightnessMinimum; + /** + * Calculates the backlight value, as recognised by the HAL, from the brightness value + * given that the rest of the system deals with. + * + * @param brightness value on the framework scale of 0-1 + * @return backlight value on the HAL scale of 0-1 + */ + public float getBacklightFromBrightness(float brightness) { + return mBrightnessToBacklightSpline.interpolate(brightness); } - public float getBrightnessMaximum() { - return mBrightnessMaximum; + /** + * Return an array of equal length to backlight and nits, that covers the entire system + * brightness range of 0.0-1.0. + * + * @return brightness array + */ + public float[] getBrightness() { + return mBrightness; } + /** + * Return the default brightness on a scale of 0.0f - 1.0f + * + * @return default brightness + */ public float getBrightnessDefault() { return mBrightnessDefault; } @@ -237,10 +274,15 @@ public class DisplayDeviceConfig { @Override public String toString() { String str = "DisplayDeviceConfig{" - + "mBrightness=" + Arrays.toString(mBrightness) + + "mBacklight=" + Arrays.toString(mBacklight) + ", mNits=" + Arrays.toString(mNits) - + ", mBrightnessMinimum=" + mBrightnessMinimum - + ", mBrightnessMaximum=" + mBrightnessMaximum + + ", mRawBacklight=" + Arrays.toString(mRawBacklight) + + ", mRawNits=" + Arrays.toString(mRawNits) + + ", mBrightness=" + Arrays.toString(mBrightness) + + ", mBrightnessToBacklightSpline=" + mBrightnessToBacklightSpline + + ", mBacklightToBrightnessSpline=" + mBacklightToBrightnessSpline + + ", mBacklightMinimum=" + mBacklightMinimum + + ", mBacklightMaximum=" + mBacklightMaximum + ", mBrightnessDefault=" + mBrightnessDefault + ", mQuirks=" + mQuirks + ", isHbmEnabled=" + mIsHighBrightnessModeEnabled @@ -253,10 +295,6 @@ public class DisplayDeviceConfig { return str; } - private float getMaxBrightness() { - return mBrightness[mBrightness.length - 1]; - } - private static DisplayDeviceConfig getConfigFromSuffix(Context context, File baseDirectory, String suffixFormat, long idNumber) { @@ -264,7 +302,6 @@ public class DisplayDeviceConfig { final String filename = String.format(CONFIG_FILE_FORMAT, suffix); final File filePath = Environment.buildPath( baseDirectory, ETC_DIR, DISPLAY_CONFIG_DIR, filename); - if (filePath.exists()) { final DisplayDeviceConfig config = new DisplayDeviceConfig(context); config.initFromFile(filePath); @@ -299,9 +336,9 @@ public class DisplayDeviceConfig { try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) { final DisplayConfiguration config = XmlParser.read(in); if (config != null) { - loadBrightnessMap(config); loadBrightnessDefaultFromDdcXml(config); loadBrightnessConstraintsFromConfigXml(); + loadBrightnessMap(config); loadHighBrightnessModeData(config); loadQuirks(config); loadBrightnessRamps(config); @@ -318,13 +355,20 @@ public class DisplayDeviceConfig { // If no ddc exists, use config.xml loadBrightnessDefaultFromConfigXml(); loadBrightnessConstraintsFromConfigXml(); + loadBrightnessMapFromConfigXml(); loadBrightnessRampsFromConfigXml(); } private void initFromPmValues() { - mBrightnessMinimum = PowerManager.BRIGHTNESS_MIN; - mBrightnessMaximum = PowerManager.BRIGHTNESS_MAX; + // Set all to basic values + mBacklightMinimum = PowerManager.BRIGHTNESS_MIN; + mBacklightMaximum = PowerManager.BRIGHTNESS_MAX; mBrightnessDefault = BRIGHTNESS_DEFAULT; + mBrightnessRampFastDecrease = PowerManager.BRIGHTNESS_MAX; + mBrightnessRampFastIncrease = PowerManager.BRIGHTNESS_MAX; + mBrightnessRampSlowDecrease = PowerManager.BRIGHTNESS_MAX; + mBrightnessRampSlowIncrease = PowerManager.BRIGHTNESS_MAX; + setSimpleMappingStrategyValues(); } private void loadBrightnessDefaultFromDdcXml(DisplayConfiguration config) { @@ -364,24 +408,27 @@ public class DisplayDeviceConfig { final float max = mContext.getResources().getFloat(com.android.internal.R.dimen .config_screenBrightnessSettingMaximumFloat); if (min == INVALID_BRIGHTNESS_IN_CONFIG || max == INVALID_BRIGHTNESS_IN_CONFIG) { - mBrightnessMinimum = BrightnessSynchronizer.brightnessIntToFloat( + mBacklightMinimum = BrightnessSynchronizer.brightnessIntToFloat( mContext.getResources().getInteger(com.android.internal.R.integer .config_screenBrightnessSettingMinimum)); - mBrightnessMaximum = BrightnessSynchronizer.brightnessIntToFloat( + mBacklightMaximum = BrightnessSynchronizer.brightnessIntToFloat( mContext.getResources().getInteger(com.android.internal.R.integer .config_screenBrightnessSettingMaximum)); } else { - mBrightnessMinimum = min; - mBrightnessMaximum = max; + mBacklightMinimum = min; + mBacklightMaximum = max; } } private void loadBrightnessMap(DisplayConfiguration config) { final NitsMap map = config.getScreenBrightnessMap(); - // Map may not exist in config file + // Map may not exist in display device config if (map == null) { + loadBrightnessMapFromConfigXml(); return; } + + // Use the (preferred) display device config mapping final List<Point> points = map.getPoint(); final int size = points.size(); @@ -408,8 +455,123 @@ public class DisplayDeviceConfig { } ++i; } - mNits = nits; - mBrightness = backlight; + mRawNits = nits; + mRawBacklight = backlight; + constrainNitsAndBacklightArrays(); + } + + private void loadBrightnessMapFromConfigXml() { + // Use the config.xml mapping + final Resources res = mContext.getResources(); + final float[] sysNits = BrightnessMappingStrategy.getFloatArray(res.obtainTypedArray( + com.android.internal.R.array.config_screenBrightnessNits)); + final int[] sysBrightness = res.getIntArray( + com.android.internal.R.array.config_screenBrightnessBacklight); + final float[] sysBrightnessFloat = new float[sysBrightness.length]; + + for (int i = 0; i < sysBrightness.length; i++) { + sysBrightnessFloat[i] = BrightnessSynchronizer.brightnessIntToFloat( + sysBrightness[i]); + } + + // These arrays are allowed to be empty, we set null values so that + // BrightnessMappingStrategy will create a SimpleMappingStrategy instead. + if (sysBrightnessFloat.length == 0 || sysNits.length == 0) { + setSimpleMappingStrategyValues(); + return; + } + + mRawNits = sysNits; + mRawBacklight = sysBrightnessFloat; + constrainNitsAndBacklightArrays(); + } + + private void setSimpleMappingStrategyValues() { + // No translation from backlight to brightness should occur if we are using a + // SimpleMappingStrategy (ie they should be the same) so the splines are + // set to be linear, between 0.0 and 1.0 + mNits = null; + mBacklight = null; + float[] simpleMappingStrategyArray = new float[]{0.0f, 1.0f}; + mBrightnessToBacklightSpline = Spline.createSpline(simpleMappingStrategyArray, + simpleMappingStrategyArray); + mBacklightToBrightnessSpline = Spline.createSpline(simpleMappingStrategyArray, + simpleMappingStrategyArray); + } + + /** + * Change the nits and backlight arrays, so that they cover only the allowed backlight values + * Use the brightness minimum and maximum values to clamp these arrays. + */ + private void constrainNitsAndBacklightArrays() { + if (mRawBacklight[0] > mBacklightMinimum + || mRawBacklight[mRawBacklight.length - 1] < mBacklightMaximum + || mBacklightMinimum > mBacklightMaximum) { + throw new IllegalStateException("Min or max values are invalid" + + "; raw min=" + mRawBacklight[0] + + "; raw max=" + mRawBacklight[mRawBacklight.length - 1] + + "; backlight min=" + mBacklightMinimum + + "; backlight max=" + mBacklightMaximum); + } + + float[] newNits = new float[mRawBacklight.length]; + float[] newBacklight = new float[mRawBacklight.length]; + // Find the starting index of the clamped arrays. This may be less than the min so + // we'll need to clamp this value still when actually doing the remapping. + int newStart = 0; + for (int i = 0; i < mRawBacklight.length - 1; i++) { + if (mRawBacklight[i + 1] > mBacklightMinimum) { + newStart = i; + break; + } + } + + boolean isLastValue = false; + int newIndex = 0; + for (int i = newStart; i < mRawBacklight.length && !isLastValue; i++) { + newIndex = i - newStart; + final float newBacklightVal; + final float newNitsVal; + isLastValue = mRawBacklight[i] > mBacklightMaximum + || i >= mRawBacklight.length - 1; + // Clamp beginning and end to valid backlight values. + if (newIndex == 0) { + newBacklightVal = MathUtils.max(mRawBacklight[i], mBacklightMinimum); + newNitsVal = rawBacklightToNits(i, newBacklightVal); + } else if (isLastValue) { + newBacklightVal = MathUtils.min(mRawBacklight[i], mBacklightMaximum); + newNitsVal = rawBacklightToNits(i - 1, newBacklightVal); + } else { + newBacklightVal = mRawBacklight[i]; + newNitsVal = mRawNits[i]; + } + newBacklight[newIndex] = newBacklightVal; + newNits[newIndex] = newNitsVal; + } + mBacklight = Arrays.copyOf(newBacklight, newIndex + 1); + mNits = Arrays.copyOf(newNits, newIndex + 1); + createBacklightConversionSplines(); + } + + private float rawBacklightToNits(int i, float backlight) { + return MathUtils.map(mRawBacklight[i], mRawBacklight[i + 1], + mRawNits[i], mRawNits[i + 1], backlight); + } + + // This method creates a brightness spline that is of equal length with proportional increments + // to the backlight spline. The values of this array range from 0.0f to 1.0f instead of the + // potential constrained range that the backlight array covers + // These splines are used to convert from the system brightness value to the HAL backlight + // value + private void createBacklightConversionSplines() { + mBrightness = new float[mBacklight.length]; + for (int i = 0; i < mBrightness.length; i++) { + mBrightness[i] = MathUtils.map(mBacklight[0], + mBacklight[mBacklight.length - 1], + PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, mBacklight[i]); + } + mBrightnessToBacklightSpline = Spline.createSpline(mBrightness, mBacklight); + mBacklightToBrightnessSpline = Spline.createSpline(mBacklight, mBrightness); } private void loadQuirks(DisplayConfiguration config) { @@ -425,12 +587,14 @@ public class DisplayDeviceConfig { mIsHighBrightnessModeEnabled = hbm.getEnabled(); mHbmData = new HighBrightnessModeData(); mHbmData.minimumLux = hbm.getMinimumLux_all().floatValue(); - mHbmData.transitionPoint = hbm.getTransitionPoint_all().floatValue(); - if (mHbmData.transitionPoint >= getMaxBrightness()) { + float transitionPointBacklightScale = hbm.getTransitionPoint_all().floatValue(); + if (transitionPointBacklightScale >= mBacklightMaximum) { throw new IllegalArgumentException("HBM transition point invalid. " + mHbmData.transitionPoint + " is not less than " - + getMaxBrightness()); + + mBacklightMaximum); } + mHbmData.transitionPoint = + mBacklightToBrightnessSpline.interpolate(transitionPointBacklightScale); final HbmTiming hbmTiming = hbm.getTiming_all(); mHbmData.timeWindowMillis = hbmTiming.getTimeWindowSecs_all().longValue() * 1000; mHbmData.timeMaxMillis = hbmTiming.getTimeMaxSecs_all().longValue() * 1000; diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 62cf86b31180..86142bc1797d 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -190,12 +190,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // The dim screen brightness. private final float mScreenBrightnessDimConfig; - // The minimum allowed brightness. - private final float mScreenBrightnessRangeMinimum; - - // The maximum allowed brightness. - private final float mScreenBrightnessRangeMaximum; - private final float mScreenBrightnessDefault; // The minimum allowed brightness while in VR. @@ -443,8 +437,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final Resources resources = context.getResources(); - final float screenBrightnessSettingMinimumFloat = clampAbsoluteBrightness( - pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM)); // DOZE AND DIM SETTINGS mScreenBrightnessDozeConfig = clampAbsoluteBrightness( @@ -453,10 +445,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM)); // NORMAL SCREEN SETTINGS - mScreenBrightnessRangeMinimum = - Math.min(screenBrightnessSettingMinimumFloat, mScreenBrightnessDimConfig); - mScreenBrightnessRangeMaximum = clampAbsoluteBrightness( - pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM)); mScreenBrightnessDefault = clampAbsoluteBrightness( mLogicalDisplay.getDisplayInfoLocked().brightnessDefault); @@ -481,18 +469,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call DisplayDeviceConfig displayDeviceConfig = logicalDisplay .getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig(); - // TODO: (b/178183143) Ensure that the ddc is not null - if (displayDeviceConfig != null) { - mBrightnessRampRateFastDecrease = displayDeviceConfig.getBrightnessRampFastDecrease(); - mBrightnessRampRateFastIncrease = displayDeviceConfig.getBrightnessRampFastIncrease(); - mBrightnessRampRateSlowDecrease = displayDeviceConfig.getBrightnessRampSlowDecrease(); - mBrightnessRampRateSlowIncrease = displayDeviceConfig.getBrightnessRampSlowIncrease(); - } else { - mBrightnessRampRateFastDecrease = 1.0f; - mBrightnessRampRateFastIncrease = 1.0f; - mBrightnessRampRateSlowDecrease = 1.0f; - mBrightnessRampRateSlowIncrease = 1.0f; - } + mBrightnessRampRateFastDecrease = displayDeviceConfig.getBrightnessRampFastDecrease(); + mBrightnessRampRateFastIncrease = displayDeviceConfig.getBrightnessRampFastIncrease(); + mBrightnessRampRateSlowDecrease = displayDeviceConfig.getBrightnessRampSlowDecrease(); + mBrightnessRampRateSlowIncrease = displayDeviceConfig.getBrightnessRampSlowIncrease(); mSkipScreenOnBrightnessRamp = resources.getBoolean( com.android.internal.R.bool.config_skipScreenOnBrightnessRamp); @@ -545,12 +525,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call com.android.internal.R.string.config_displayLightSensorType); Sensor lightSensor = findDisplayLightSensor(lightSensorType); - mBrightnessMapper = BrightnessMappingStrategy.create(resources); + final DisplayDeviceConfig ddc = + logicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig(); + mBrightnessMapper = BrightnessMappingStrategy.create(resources, ddc); if (mBrightnessMapper != null) { mAutomaticBrightnessController = new AutomaticBrightnessController(this, handler.getLooper(), sensorManager, lightSensor, mBrightnessMapper, - lightSensorWarmUpTimeConfig, mScreenBrightnessRangeMinimum, - mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate, + lightSensorWarmUpTimeConfig, PowerManager.BRIGHTNESS_MIN, + PowerManager.BRIGHTNESS_MAX, dozeScaleFactor, lightSensorRate, initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds, screenBrightnessThresholds, logicalDisplay, context); @@ -838,9 +820,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call noteScreenBrightness(mPowerState.getScreenBrightness()); // Initialize all of the brightness tracking state - final float brightness = convertToNits(BrightnessSynchronizer.brightnessFloatToInt( - mPowerState.getScreenBrightness())); - if (brightness >= 0.0f) { + final float brightness = convertToNits(mPowerState.getScreenBrightness()); + if (brightness >= PowerManager.BRIGHTNESS_MIN) { mBrightnessTracker.start(brightness); } @@ -1151,10 +1132,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Apply dimming by at least some minimum amount when user activity // timeout is about to expire. if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) { - if (brightnessState > mScreenBrightnessRangeMinimum) { + if (brightnessState > PowerManager.BRIGHTNESS_MIN) { brightnessState = Math.max(Math.min(brightnessState - SCREEN_DIM_MINIMUM_REDUCTION_FLOAT, - mScreenBrightnessDimConfig), mScreenBrightnessRangeMinimum); + mScreenBrightnessDimConfig), PowerManager.BRIGHTNESS_MIN); mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_DIMMED); } if (!mAppliedDimming) { @@ -1168,12 +1149,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // If low power mode is enabled, scale brightness by screenLowPowerBrightnessFactor // as long as it is above the minimum threshold. if (mPowerRequest.lowPowerMode) { - if (brightnessState > mScreenBrightnessRangeMinimum) { + if (brightnessState > PowerManager.BRIGHTNESS_MIN) { final float brightnessFactor = Math.min(mPowerRequest.screenLowPowerBrightnessFactor, 1); final float lowPowerBrightnessFloat = (brightnessState * brightnessFactor); - brightnessState = Math.max(lowPowerBrightnessFloat, - mScreenBrightnessRangeMinimum); + brightnessState = Math.max(lowPowerBrightnessFloat, PowerManager.BRIGHTNESS_MIN); mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_LOW_POWER); } if (!mAppliedLowPower) { @@ -1258,9 +1238,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // slider event so notify as if the system changed the brightness. userInitiatedChange = false; } - notifyBrightnessChanged( - BrightnessSynchronizer.brightnessFloatToInt(brightnessState), - userInitiatedChange, hadUserBrightnessPoint); + notifyBrightnessChanged(brightnessState, userInitiatedChange, + hadUserBrightnessPoint); } } @@ -1487,17 +1466,17 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private float clampScreenBrightness(float value) { if (Float.isNaN(value)) { - return mScreenBrightnessRangeMinimum; + return PowerManager.BRIGHTNESS_MIN; } return MathUtils.constrain( - value, mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum); + value, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); } // Checks whether the brightness is within the valid brightness range, not including the off or // invalid states. private boolean isValidBrightnessValue(float brightnessState) { - return brightnessState >= mScreenBrightnessRangeMinimum - && brightnessState <= mScreenBrightnessRangeMaximum; + return brightnessState >= PowerManager.BRIGHTNESS_MIN + && brightnessState <= PowerManager.BRIGHTNESS_MAX; } private void animateScreenBrightness(float target, float rate) { @@ -1874,7 +1853,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call return true; } - private void notifyBrightnessChanged(int brightness, boolean userInitiated, + private void notifyBrightnessChanged(float brightness, boolean userInitiated, boolean hadUserDataPoint) { final float brightnessInNits = convertToNits(brightness); if (mPowerRequest.useAutoBrightness && brightnessInNits >= 0.0f @@ -1891,9 +1870,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } - private float convertToNits(int backlight) { + private float convertToNits(float brightness) { if (mBrightnessMapper != null) { - return mBrightnessMapper.convertToNits(backlight); + return mBrightnessMapper.convertToNits(brightness); } else { return -1.0f; } @@ -1972,12 +1951,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println(); pw.println("Display Power Controller Configuration:"); - pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum); - pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum); pw.println(" mScreenBrightnessRangeDefault=" + mScreenBrightnessDefault); pw.println(" mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig); pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig); - pw.println(" mScreenBrightnessDefault=" + mScreenBrightnessDefault); pw.println(" mScreenBrightnessForVrRangeMinimum=" + mScreenBrightnessForVrRangeMinimum); pw.println(" mScreenBrightnessForVrRangeMaximum=" + mScreenBrightnessForVrRangeMaximum); pw.println(" mScreenBrightnessForVrDefault=" + mScreenBrightnessForVrDefault); @@ -2109,10 +2085,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } - private static int clampAbsoluteBrightness(int value) { - return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON); - } - private static float clampAbsoluteBrightness(float value) { return MathUtils.constrain(value, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 5b2b3366b117..3709963b7caa 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -30,7 +30,6 @@ import android.os.Trace; import android.util.LongSparseArray; import android.util.Slog; import android.util.SparseArray; -import android.util.Spline; import android.view.Display; import android.view.DisplayAddress; import android.view.DisplayCutout; @@ -210,8 +209,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { private SurfaceControl.DisplayMode[] mSfDisplayModes; // The active display mode in SurfaceFlinger private SurfaceControl.DisplayMode mActiveSfDisplayMode; - private Spline mSystemBrightnessToNits; - private Spline mNitsToHalBrightness; + private DisplayDeviceConfig mDisplayDeviceConfig; private DisplayEventReceiver.FrameRateOverride[] mFrameRateOverrides = new DisplayEventReceiver.FrameRateOverride[0]; @@ -410,16 +408,13 @@ final class LocalDisplayAdapter extends DisplayAdapter { @Override public DisplayDeviceConfig getDisplayDeviceConfig() { if (mDisplayDeviceConfig == null) { - loadDisplayConfiguration(); + loadDisplayDeviceConfig(); } return mDisplayDeviceConfig; } - private void loadDisplayConfiguration() { - Spline nitsToHal = null; - Spline sysToNits = null; - - // Load the mapping from nits to HAL brightness range (display-device-config.xml) + private void loadDisplayDeviceConfig() { + // Load display device config final Context context = getOverlayContext(); mDisplayDeviceConfig = DisplayDeviceConfig.create(context, mPhysicalDisplayId, mIsDefaultDisplay); @@ -427,33 +422,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { return; } + // Load brightness HWC quirk mBacklightAdapter.setForceSurfaceControl(mDisplayDeviceConfig.hasQuirk( DisplayDeviceConfig.QUIRK_CAN_SET_BRIGHTNESS_VIA_HWC)); - - final float[] halNits = mDisplayDeviceConfig.getNits(); - final float[] halBrightness = mDisplayDeviceConfig.getBrightness(); - if (halNits == null || halBrightness == null) { - return; - } - nitsToHal = Spline.createSpline(halNits, halBrightness); - - // Load the mapping from system brightness range to nits (config.xml) - final Resources res = context.getResources(); - final float[] sysNits = BrightnessMappingStrategy.getFloatArray(res.obtainTypedArray( - com.android.internal.R.array.config_screenBrightnessNits)); - final int[] sysBrightness = res.getIntArray( - com.android.internal.R.array.config_screenBrightnessBacklight); - if (sysNits.length == 0 || sysBrightness.length != sysNits.length) { - return; - } - final float[] sysBrightnessFloat = new float[sysBrightness.length]; - for (int i = 0; i < sysBrightness.length; i++) { - sysBrightnessFloat[i] = sysBrightness[i]; - } - sysToNits = Spline.createSpline(sysBrightnessFloat, sysNits); - - mNitsToHalBrightness = nitsToHal; - mSystemBrightnessToNits = sysToNits; } private boolean updateStaticInfo(SurfaceControl.StaticDisplayInfo info) { @@ -665,15 +636,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { // The display is trusted since it is created by system. mInfo.flags |= DisplayDeviceInfo.FLAG_TRUSTED; - if (mDisplayDeviceConfig != null) { - mInfo.brightnessMinimum = mDisplayDeviceConfig.getBrightnessMinimum(); - mInfo.brightnessMaximum = mDisplayDeviceConfig.getBrightnessMaximum(); - mInfo.brightnessDefault = mDisplayDeviceConfig.getBrightnessDefault(); - } else { - mInfo.brightnessMinimum = PowerManager.BRIGHTNESS_MIN; - mInfo.brightnessMaximum = PowerManager.BRIGHTNESS_MAX; - mInfo.brightnessDefault = 0.5f; - } + mInfo.brightnessMinimum = PowerManager.BRIGHTNESS_MIN; + mInfo.brightnessMaximum = PowerManager.BRIGHTNESS_MAX; + mInfo.brightnessDefault = getDisplayDeviceConfig().getBrightnessDefault(); } return mInfo; } @@ -811,8 +776,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness(" + "id=" + physicalDisplayId + ", brightness=" + brightness + ")"); try { - brightness = displayBrightnessToHalBrightness(brightness); - mBacklightAdapter.setBrightness(brightness); + float backlight = brightnessToBacklight(brightness); + mBacklightAdapter.setBacklight(backlight); Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenBrightness", BrightnessSynchronizer.brightnessFloatToInt(brightness)); @@ -821,35 +786,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } - /** - * Converts brightness range from the framework's brightness space to the - * Hal brightness space if the HAL brightness space has been provided via - * a display device configuration file. - */ - private float displayBrightnessToHalBrightness(float brightness) { - // TODO: b/171380847 - This needs to be deprecated. The nits-to-brightness - // relationship should be specified in display-config OR config.xml, but not - // both, and no nits-space conversion should be necessary. - // - // Only do a conversion if there exists a unique system brightness and a - // unique HAL brightness-to-nits range defined. - if (mSystemBrightnessToNits == null || mNitsToHalBrightness == null) { - return brightness; - } - - // Sys brightness in this conversion is always specified in the old 1-255 - // range, so convert that here before the translation. - final float brightnessInt = - BrightnessSynchronizer.brightnessFloatToIntRange(brightness); - - if (BrightnessSynchronizer.floatEquals( - brightnessInt, PowerManager.BRIGHTNESS_OFF)) { - return PowerManager.BRIGHTNESS_OFF_FLOAT; - } - - final float nits = mSystemBrightnessToNits.interpolate(brightnessInt); - final float halBrightness = mNitsToHalBrightness.interpolate(nits); - return halBrightness; + private float brightnessToBacklight(float brightness) { + return getDisplayDeviceConfig().getBacklightFromBrightness(brightness); } }; } @@ -1338,11 +1276,12 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } - void setBrightness(float brightness) { + // Set backlight within min and max backlight values + void setBacklight(float backlight) { if (mUseSurfaceControlBrightness || mForceSurfaceControl) { - mSurfaceControlProxy.setDisplayBrightness(mDisplayToken, brightness); + mSurfaceControlProxy.setDisplayBrightness(mDisplayToken, backlight); } else if (mBacklight != null) { - mBacklight.setBrightness(brightness); + mBacklight.setBrightness(backlight); } } diff --git a/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java b/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java index d514aab31e8d..62337c7df03b 100644 --- a/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java +++ b/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java @@ -46,7 +46,7 @@ import java.util.Set; private static final String ATTR_VALUE = "value"; /* package */ static class Config { - public long lastModifiedDate; + public long lastModifiedMillis; public final Set<String> updatedFontDirs = new ArraySet<>(); public final List<FontConfig.FontFamily> fontFamilies = new ArrayList<>(); } @@ -73,7 +73,7 @@ import java.util.Set; } else if (depth == 2) { switch (tag) { case TAG_LAST_MODIFIED_DATE: - out.lastModifiedDate = parseLongAttribute(parser, ATTR_VALUE, 0); + out.lastModifiedMillis = parseLongAttribute(parser, ATTR_VALUE, 0); break; case TAG_UPDATED_FONT_DIR: out.updatedFontDirs.add(getAttribute(parser, ATTR_VALUE)); @@ -101,7 +101,7 @@ import java.util.Set; out.startTag(null, TAG_ROOT); out.startTag(null, TAG_LAST_MODIFIED_DATE); - out.attribute(null, ATTR_VALUE, Long.toString(config.lastModifiedDate)); + out.attribute(null, ATTR_VALUE, Long.toString(config.lastModifiedMillis)); out.endTag(null, TAG_LAST_MODIFIED_DATE); for (String dir : config.updatedFontDirs) { out.startTag(null, TAG_UPDATED_FONT_DIR); diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java index 08ddc6ddf4ae..86dbe86f85ee 100644 --- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java +++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java @@ -112,7 +112,7 @@ final class UpdatableFontDir { private final File mConfigFile; private final File mTmpConfigFile; - private long mLastModifiedDate; + private long mLastModifiedMillis; private int mConfigVersion = 1; /** @@ -147,7 +147,7 @@ final class UpdatableFontDir { /* package */ void loadFontFileMap() { mFontFileInfoMap.clear(); mFontFamilyMap.clear(); - mLastModifiedDate = 0; + mLastModifiedMillis = 0; boolean success = false; try { PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config(); @@ -157,7 +157,7 @@ final class UpdatableFontDir { Slog.e(TAG, "Failed to load config xml file", e); return; } - mLastModifiedDate = config.lastModifiedDate; + mLastModifiedMillis = config.lastModifiedMillis; File[] dirs = mFilesDir.listFiles(); if (dirs == null) return; @@ -200,7 +200,7 @@ final class UpdatableFontDir { if (!success) { mFontFileInfoMap.clear(); mFontFamilyMap.clear(); - mLastModifiedDate = 0; + mLastModifiedMillis = 0; FileUtils.deleteContents(mFilesDir); } } @@ -211,7 +211,7 @@ final class UpdatableFontDir { FileUtils.deleteContents(mFilesDir); mFontFamilyMap.clear(); - mLastModifiedDate = Instant.now().getEpochSecond(); + mLastModifiedMillis = System.currentTimeMillis(); try (FileOutputStream fos = new FileOutputStream(mConfigFile)) { PersistentSystemFontConfig.writeToXml(fos, createPersistentConfig()); } catch (Exception e) { @@ -231,7 +231,7 @@ final class UpdatableFontDir { // Backup the mapping for rollback. ArrayMap<String, FontFileInfo> backupMap = new ArrayMap<>(mFontFileInfoMap); ArrayMap<String, FontConfig.FontFamily> backupFamilies = new ArrayMap<>(mFontFamilyMap); - long backupLastModifiedDate = mLastModifiedDate; + long backupLastModifiedDate = mLastModifiedMillis; boolean success = false; try { for (FontUpdateRequest request : requests) { @@ -247,7 +247,7 @@ final class UpdatableFontDir { } // Write config file. - mLastModifiedDate = Instant.now().getEpochSecond(); + mLastModifiedMillis = Instant.now().getEpochSecond(); try (FileOutputStream fos = new FileOutputStream(mTmpConfigFile)) { PersistentSystemFontConfig.writeToXml(fos, createPersistentConfig()); } catch (Exception e) { @@ -269,7 +269,7 @@ final class UpdatableFontDir { mFontFileInfoMap.putAll(backupMap); mFontFamilyMap.clear(); mFontFamilyMap.putAll(backupFamilies); - mLastModifiedDate = backupLastModifiedDate; + mLastModifiedMillis = backupLastModifiedDate; } } } @@ -527,7 +527,7 @@ final class UpdatableFontDir { private PersistentSystemFontConfig.Config createPersistentConfig() { PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config(); - config.lastModifiedDate = mLastModifiedDate; + config.lastModifiedMillis = mLastModifiedMillis; for (FontFileInfo info : mFontFileInfoMap.values()) { config.updatedFontDirs.add(info.getRandomizedFontDir().getName()); } @@ -560,7 +560,7 @@ final class UpdatableFontDir { // which will be used as a fallback font without being overridden. mergedFamilies.addAll(mFontFamilyMap.values()); return new FontConfig( - mergedFamilies, config.getAliases(), mLastModifiedDate, mConfigVersion); + mergedFamilies, config.getAliases(), mLastModifiedMillis, mConfigVersion); } /* package */ int getConfigVersion() { 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/InputShellCommand.java b/services/core/java/com/android/server/input/InputShellCommand.java index d08980cfbf24..720be826fd38 100644 --- a/services/core/java/com/android/server/input/InputShellCommand.java +++ b/services/core/java/com/android/server/input/InputShellCommand.java @@ -29,6 +29,7 @@ import android.view.MotionEvent; import android.view.ViewConfiguration; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -199,6 +200,8 @@ public class InputShellCommand extends ShellCommand { runRoll(inputSource, displayId); } else if ("motionevent".equals(arg)) { runMotionEvent(inputSource, displayId); + } else if ("keycombination".equals(arg)) { + runKeyCombination(inputSource, displayId); } else { handleDefaultCommands(arg); } @@ -224,7 +227,7 @@ public class InputShellCommand extends ShellCommand { out.println(); out.println("The commands and default sources are:"); out.println(" text <string> (Default: touchscreen)"); - out.println(" keyevent [--longpress] <key code number or name> ..." + out.println(" keyevent [--longpress|--doubletap] <key code number or name> ..." + " (Default: keyboard)"); out.println(" tap <x> <y> (Default: touchscreen)"); out.println(" swipe <x1> <y1> <x2> <y2> [duration(ms)]" @@ -234,6 +237,8 @@ public class InputShellCommand extends ShellCommand { out.println(" press (Default: trackball)"); out.println(" roll <dx> <dy> (Default: trackball)"); out.println(" motionevent <DOWN|UP|MOVE|CANCEL> <x> <y> (Default: touchscreen)"); + out.println(" keycombination <key code 1> <key code 2> ..." + + " (Default: keyboard)"); } } @@ -282,6 +287,14 @@ public class InputShellCommand extends ShellCommand { final boolean longpress = "--longpress".equals(arg); if (longpress) { arg = getNextArgRequired(); + } else { + final boolean doubleTap = "--doubletap".equals(arg); + if (doubleTap) { + arg = getNextArgRequired(); + final int keycode = KeyEvent.keyCodeFromString(arg); + sendKeyDoubleTap(inputSource, keycode, displayId); + return; + } } do { @@ -292,22 +305,32 @@ public class InputShellCommand extends ShellCommand { private void sendKeyEvent(int inputSource, int keyCode, boolean longpress, int displayId) { final long now = SystemClock.uptimeMillis(); - int repeatCount = 0; - KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, repeatCount, + KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0 /* repeatCount */, 0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/, inputSource); event.setDisplayId(displayId); injectKeyEvent(event); if (longpress) { - repeatCount++; - injectKeyEvent(KeyEvent.changeTimeRepeat(event, now, repeatCount, + // Some long press behavior would check the event time, we set a new event time here. + final long nextEventTime = now + ViewConfiguration.getGlobalActionKeyTimeout(); + injectKeyEvent(KeyEvent.changeTimeRepeat(event, nextEventTime, 1 /* repeatCount */, KeyEvent.FLAG_LONG_PRESS)); } injectKeyEvent(KeyEvent.changeAction(event, KeyEvent.ACTION_UP)); } + private void sendKeyDoubleTap(int inputSource, int keyCode, int displayId) { + sendKeyEvent(inputSource, keyCode, false, displayId); + try { + Thread.sleep(ViewConfiguration.getDoubleTapMinTime()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + sendKeyEvent(inputSource, keyCode, false, displayId); + } + private void runTap(int inputSource, int displayId) { inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN); sendTap(inputSource, Float.parseFloat(getNextArgRequired()), @@ -440,4 +463,59 @@ public class InputShellCommand extends ShellCommand { final long now = SystemClock.uptimeMillis(); injectMotionEvent(inputSource, action, now, now, x, y, pressure, displayId); } + + private void runKeyCombination(int inputSource, int displayId) { + String arg = getNextArgRequired(); + ArrayList<Integer> keyCodes = new ArrayList<>(); + + while (arg != null) { + final int keyCode = KeyEvent.keyCodeFromString(arg); + if (keyCode == KeyEvent.KEYCODE_UNKNOWN) { + throw new IllegalArgumentException("Unknown keycode: " + arg); + } + keyCodes.add(keyCode); + arg = getNextArg(); + } + + // At least 2 keys. + if (keyCodes.size() < 2) { + throw new IllegalArgumentException("keycombination requires at least 2 keycodes"); + } + + sendKeyCombination(inputSource, keyCodes, displayId); + } + + private void injectKeyEventAsync(KeyEvent event) { + InputManager.getInstance().injectInputEvent(event, + InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + } + + private void sendKeyCombination(int inputSource, ArrayList<Integer> keyCodes, int displayId) { + final long now = SystemClock.uptimeMillis(); + final int count = keyCodes.size(); + final KeyEvent[] events = new KeyEvent[count]; + for (int i = 0; i < count; i++) { + final KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCodes.get(i), 0, + 0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/, + inputSource); + event.setDisplayId(displayId); + events[i] = event; + } + + for (KeyEvent event: events) { + // Use async inject so interceptKeyBeforeQueueing or interceptKeyBeforeDispatching could + // handle keys. + injectKeyEventAsync(event); + } + + try { + Thread.sleep(ViewConfiguration.getTapTimeout()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + for (KeyEvent event: events) { + injectKeyEventAsync(KeyEvent.changeAction(event, KeyEvent.ACTION_UP)); + } + } } diff --git a/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java b/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java index 7b400b6e0309..6ea4bd2b1d6d 100644 --- a/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java +++ b/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java @@ -17,7 +17,6 @@ package com.android.server.location; import android.annotation.Nullable; -import android.content.ComponentName; import android.content.Context; import android.hardware.location.ActivityRecognitionHardware; import android.hardware.location.IActivityRecognitionHardwareClient; @@ -27,6 +26,7 @@ import android.os.RemoteException; import android.util.Log; import com.android.server.ServiceWatcher; +import com.android.server.ServiceWatcher.BoundService; /** * Proxy class to bind GmsCore to the ActivityRecognitionHardware. @@ -82,7 +82,7 @@ public class HardwareActivityRecognitionProxy { return resolves; } - private void onBind(IBinder binder, ComponentName service) throws RemoteException { + private void onBind(IBinder binder, BoundService service) throws RemoteException { String descriptor = binder.getInterfaceDescriptor(); if (IActivityRecognitionHardwareWatcher.class.getCanonicalName().equals(descriptor)) { diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index cbbc981d9ed6..57e9fc9cb719 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -63,6 +63,7 @@ import android.location.LastLocationRequest; import android.location.Location; import android.location.LocationManager; import android.location.LocationManagerInternal; +import android.location.LocationManagerInternal.OnProviderLocationTagsChangeListener; import android.location.LocationProvider; import android.location.LocationRequest; import android.location.LocationTime; @@ -82,6 +83,7 @@ import android.os.WorkSource.WorkChain; import android.provider.Settings; import android.stats.location.LocationStatsEnums; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.Log; @@ -255,6 +257,9 @@ public class LocationManagerService extends ILocationManager.Stub { final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers = new CopyOnWriteArrayList<>(); + @GuardedBy("mLock") + private @Nullable OnProviderLocationTagsChangeListener mOnProviderLocationTagsChangeListener; + LocationManagerService(Context context, Injector injector, LocationEventLog eventLog) { mContext = context.createAttributionContext(ATTRIBUTION_TAG); mInjector = injector; @@ -319,8 +324,9 @@ public class LocationManagerService extends ILocationManager.Stub { Preconditions.checkState(getLocationProviderManager(manager.getName()) == null); manager.startManager(); + manager.setOnProviderLocationTagsChangeListener( + mOnProviderLocationTagsChangeListener); if (realProvider != null) { - // custom logic wrapping all non-passive providers if (manager != mPassiveManager) { boolean enableStationaryThrottling = Settings.Global.getInt( @@ -331,7 +337,6 @@ public class LocationManagerService extends ILocationManager.Stub { mInjector, realProvider, mEventLog); } } - manager.setRealProvider(realProvider); } mProviderManagers.add(manager); @@ -456,8 +461,9 @@ public class LocationManagerService extends ILocationManager.Stub { .setPowerUsage(Integer.parseInt(fragments[8])) .setAccuracy(Integer.parseInt(fragments[9])) .build(); - getOrAddLocationProviderManager(name).setMockProvider( - new MockLocationProvider(properties, CallerIdentity.fromContext(mContext))); + final LocationProviderManager manager = getOrAddLocationProviderManager(name); + manager.setMockProvider(new MockLocationProvider(properties, + CallerIdentity.fromContext(mContext), /*locationTags*/ null)); } } @@ -1147,15 +1153,16 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void addTestProvider(String provider, ProviderProperties properties, - String packageName, String attributionTag) { + List<String> locationTags, String packageName, String attributionTag) { // unsafe is ok because app ops will verify the package name CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag); if (!mInjector.getAppOpsHelper().noteOp(AppOpsManager.OP_MOCK_LOCATION, identity)) { return; } - getOrAddLocationProviderManager(provider).setMockProvider( - new MockLocationProvider(properties, identity)); + final LocationProviderManager manager = getOrAddLocationProviderManager(provider); + manager.setMockProvider(new MockLocationProvider(properties, identity, + (locationTags != null) ? new ArraySet<>(locationTags) : null)); } @Override @@ -1392,6 +1399,19 @@ public class LocationManagerService extends ILocationManager.Stub { return new LocationTime(location.getTime(), location.getElapsedRealtimeNanos()); } + + @Override + public void setOnProviderLocationTagsChangeListener( + @Nullable OnProviderLocationTagsChangeListener listener) { + synchronized (mLock) { + mOnProviderLocationTagsChangeListener = listener; + final int providerCount = mProviderManagers.size(); + for (int i = 0; i < providerCount; i++) { + final LocationProviderManager manager = mProviderManagers.get(i); + manager.setOnProviderLocationTagsChangeListener(listener); + } + } + } } private static class SystemInjector implements Injector { diff --git a/services/core/java/com/android/server/location/LocationShellCommand.java b/services/core/java/com/android/server/location/LocationShellCommand.java index f0dd8b559d64..21a9b0442b74 100644 --- a/services/core/java/com/android/server/location/LocationShellCommand.java +++ b/services/core/java/com/android/server/location/LocationShellCommand.java @@ -75,8 +75,8 @@ class LocationShellCommand extends BasicShellCommandHandler { case "add-test-provider": { String provider = getNextArgRequired(); ProviderProperties properties = parseTestProviderProviderProperties(); - mService.addTestProvider(provider, properties, mContext.getOpPackageName(), - mContext.getFeatureId()); + mService.addTestProvider(provider, properties, /*locationTags*/ null, + mContext.getOpPackageName(), mContext.getFeatureId()); return 0; } case "remove-test-provider": { diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java index 81c1e45504cb..dde45c4ab52a 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java @@ -295,7 +295,7 @@ public class ContextHubService extends IContextHubService.Stub { }; SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext); manager.addSensorPrivacyListener( - SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE, listener); + SensorPrivacyManager.Sensors.MICROPHONE, listener); } } @@ -1079,8 +1079,8 @@ public class ContextHubService extends IContextHubService.Stub { */ private void sendMicrophoneDisableSettingUpdate() { SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext); - boolean disabled = manager.isIndividualSensorPrivacyEnabled( - SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE); + boolean disabled = manager.isSensorPrivacyEnabled( + SensorPrivacyManager.Sensors.MICROPHONE); Log.d(TAG, "Mic Disabled Setting: " + disabled); mContextHubWrapper.onMicrophoneDisableSettingChanged(disabled); } diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java index 9216a6b245a6..29da177ee4a1 100644 --- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java @@ -371,7 +371,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements public GnssLocationProvider(Context context, Injector injector, GnssNative gnssNative, GnssMetrics gnssMetrics) { - super(FgThread.getExecutor(), CallerIdentity.fromContext(context), PROPERTIES); + super(FgThread.getExecutor(), CallerIdentity.fromContext(context), PROPERTIES, + /*locationTags*/ null); mContext = context; mGnssNative = gnssNative; 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 08deb8698608..9ff6e6bc8e32 100644 --- a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java +++ b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java @@ -29,6 +29,7 @@ import com.android.internal.util.Preconditions; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Objects; +import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicReference; import java.util.function.UnaryOperator; @@ -67,7 +68,7 @@ public abstract class AbstractLocationProvider { * Default state value for a location provider that is disabled with no properties and an * empty provider package list. */ - public static final State EMPTY_STATE = new State(false, null, null); + public static final State EMPTY_STATE = new State(false, null, null, null); /** * The provider's allowed state. @@ -84,10 +85,14 @@ public abstract class AbstractLocationProvider { */ @Nullable public final CallerIdentity identity; - private State(boolean allowed, ProviderProperties properties, CallerIdentity identity) { + @Nullable public final Set<String> locationTags; + + private State(boolean allowed, ProviderProperties properties, CallerIdentity identity, + Set<String> locationTags) { this.allowed = allowed; this.properties = properties; this.identity = identity; + this.locationTags = locationTags; } /** @@ -97,7 +102,7 @@ public abstract class AbstractLocationProvider { if (allowed == this.allowed) { return this; } else { - return new State(allowed, properties, identity); + return new State(allowed, properties, identity, locationTags); } } @@ -108,7 +113,7 @@ public abstract class AbstractLocationProvider { if (Objects.equals(properties, this.properties)) { return this; } else { - return new State(allowed, properties, identity); + return new State(allowed, properties, identity, locationTags); } } @@ -119,10 +124,22 @@ public abstract class AbstractLocationProvider { if (Objects.equals(identity, this.identity)) { return this; } else { - return new State(allowed, properties, identity); + return new State(allowed, properties, identity, locationTags); } } + /** + * Returns a state the same as the current but with location tags set as specified. + */ + public State withLocationTags(@Nullable Set<String> locationTags) { + if (Objects.equals(locationTags, this.locationTags)) { + return this; + } else { + return new State(allowed, properties, identity, locationTags); + } + } + + @Override public boolean equals(Object o) { if (this == o) { @@ -133,12 +150,13 @@ public abstract class AbstractLocationProvider { } State state = (State) o; return allowed == state.allowed && properties == state.properties - && Objects.equals(identity, state.identity); + && Objects.equals(identity, state.identity) + && Objects.equals(locationTags, state.locationTags); } @Override public int hashCode() { - return Objects.hash(allowed, properties, identity); + return Objects.hash(allowed, properties, identity, locationTags); } } @@ -195,13 +213,14 @@ public abstract class AbstractLocationProvider { * An optional identity and properties may be provided to initialize the location provider. */ protected AbstractLocationProvider(Executor executor, @Nullable CallerIdentity identity, - @Nullable ProviderProperties properties) { + @Nullable ProviderProperties properties, @Nullable Set<String> locationTags) { Preconditions.checkArgument(identity == null || identity.getListenerId() == null); mExecutor = executor; mInternalState = new AtomicReference<>(new InternalState(null, State.EMPTY_STATE .withIdentity(identity) - .withProperties(properties))); + .withProperties(properties).withLocationTags(locationTags)) + ); mController = new Controller(); } @@ -281,6 +300,13 @@ public abstract class AbstractLocationProvider { } /** + * Call this method to report a change in provider location tags. + */ + protected void setLocationTags(@Nullable Set<String> locationTags) { + setState(state -> state.withLocationTags(locationTags)); + } + + /** * Call this method to report a new location. */ protected void reportLocation(LocationResult locationResult) { diff --git a/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java b/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java index a3ec867220dd..49f6e64a1e0c 100644 --- a/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java +++ b/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java @@ -40,7 +40,7 @@ class DelegateLocationProvider extends AbstractLocationProvider private boolean mInitialized = false; DelegateLocationProvider(Executor executor, AbstractLocationProvider delegate) { - super(executor, null, null); + super(executor, null, null, null); mDelegate = delegate; } 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 5a35d7f88ff3..1ecf3ee40a53 100644 --- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java @@ -52,6 +52,8 @@ import android.location.LastLocationRequest; import android.location.Location; import android.location.LocationManager; import android.location.LocationManagerInternal; +import android.location.LocationManagerInternal.LocationTagInfo; +import android.location.LocationManagerInternal.OnProviderLocationTagsChangeListener; import android.location.LocationManagerInternal.ProviderEnabledListener; import android.location.LocationRequest; import android.location.LocationResult; @@ -85,6 +87,7 @@ import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; +import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.location.LocationPermissions; @@ -117,6 +120,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Predicate; @@ -1288,6 +1292,9 @@ public class LocationProviderManager extends @GuardedBy("mLock") private @Nullable OnAlarmListener mDelayedRegister; + @GuardedBy("mLock") + private @Nullable OnProviderLocationTagsChangeListener mOnLocationTagsChangeListener; + public LocationProviderManager(Context context, Injector injector, LocationEventLog eventLog, String name, @Nullable PassiveLocationProviderManager passiveManager) { mContext = context; @@ -1448,6 +1455,19 @@ public class LocationProviderManager extends } } + /** + * Registers a listener for the location tags of the provider. + * + * @param listener The listener + */ + public void setOnProviderLocationTagsChangeListener( + @Nullable OnProviderLocationTagsChangeListener listener) { + Preconditions.checkArgument(mOnLocationTagsChangeListener == null || listener == null); + synchronized (mLock) { + mOnLocationTagsChangeListener = listener; + } + } + public void setMockProvider(@Nullable MockLocationProvider provider) { synchronized (mLock) { Preconditions.checkState(mState != STATE_STOPPED); @@ -2244,6 +2264,27 @@ public class LocationProviderManager extends if (oldState.allowed != newState.allowed) { onEnabledChanged(UserHandle.USER_ALL); } + + if (!Objects.equals(oldState.locationTags, newState.locationTags)) { + if (mOnLocationTagsChangeListener != null) { + if (oldState.identity != null) { + FgThread.getHandler().sendMessage(PooledLambda.obtainMessage( + OnProviderLocationTagsChangeListener::onLocationTagsChanged, + mOnLocationTagsChangeListener, new LocationTagInfo( + oldState.identity.getUid(), oldState.identity.getPackageName(), + Collections.emptySet()) + )); + } + if (newState.identity != null) { + FgThread.getHandler().sendMessage(PooledLambda.obtainMessage( + OnProviderLocationTagsChangeListener::onLocationTagsChanged, + mOnLocationTagsChangeListener, new LocationTagInfo( + newState.identity.getUid(), newState.identity.getPackageName(), + newState.locationTags) + )); + } + } + } } @GuardedBy("mLock") diff --git a/services/core/java/com/android/server/location/provider/MockLocationProvider.java b/services/core/java/com/android/server/location/provider/MockLocationProvider.java index 0d8f64377db0..7660f56f1580 100644 --- a/services/core/java/com/android/server/location/provider/MockLocationProvider.java +++ b/services/core/java/com/android/server/location/provider/MockLocationProvider.java @@ -28,6 +28,7 @@ import android.os.Bundle; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.Set; /** * A mock location provider used by LocationManagerService to implement test providers. @@ -38,9 +39,10 @@ public class MockLocationProvider extends AbstractLocationProvider { @Nullable private Location mLocation; - public MockLocationProvider(ProviderProperties properties, CallerIdentity identity) { + public MockLocationProvider(ProviderProperties properties, CallerIdentity identity, + @Nullable Set<String> locationTags) { // using a direct executor is ok because this class has no locks that could deadlock - super(DIRECT_EXECUTOR, identity, properties); + super(DIRECT_EXECUTOR, identity, properties, locationTags); } /** Sets the allowed state of this mock provider. */ diff --git a/services/core/java/com/android/server/location/provider/MockableLocationProvider.java b/services/core/java/com/android/server/location/provider/MockableLocationProvider.java index cb7264e55fa9..4ffa9a509a23 100644 --- a/services/core/java/com/android/server/location/provider/MockableLocationProvider.java +++ b/services/core/java/com/android/server/location/provider/MockableLocationProvider.java @@ -75,7 +75,7 @@ public class MockableLocationProvider extends AbstractLocationProvider { public MockableLocationProvider(Object ownerLock) { // using a direct executor is acceptable because all inbound calls are delegated to the // actual provider implementations which will use their own executors - super(DIRECT_EXECUTOR, null, null); + super(DIRECT_EXECUTOR, null, null, null); mOwnerLock = ownerLock; mRequest = ProviderRequest.EMPTY_REQUEST; } diff --git a/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java b/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java index a5758a37b983..ee9d35d21798 100644 --- a/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java +++ b/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java @@ -47,7 +47,8 @@ public class PassiveLocationProvider extends AbstractLocationProvider { public PassiveLocationProvider(Context context) { // using a direct executor is ok because this class has no locks that could deadlock - super(DIRECT_EXECUTOR, CallerIdentity.fromContext(context), PROPERTIES); + super(DIRECT_EXECUTOR, CallerIdentity.fromContext(context), PROPERTIES, + /*locationTags*/ null); setAllowed(true); } diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java index 4c97f645aac9..f00478a3488a 100644 --- a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java +++ b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java @@ -18,6 +18,7 @@ package com.android.server.location.provider.proxy; import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; @@ -32,10 +33,12 @@ import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; +import android.util.ArraySet; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.ArrayUtils; import com.android.server.ServiceWatcher; +import com.android.server.ServiceWatcher.BoundService; import com.android.server.location.provider.AbstractLocationProvider; import java.io.FileDescriptor; @@ -49,6 +52,9 @@ import java.util.Objects; */ public class ProxyLocationProvider extends AbstractLocationProvider { + private static final String KEY_LOCATION_TAGS = "android:location_allow_listed_tags"; + private static final String LOCATION_TAGS_SEPARATOR = ";"; + /** * Creates and registers this proxy. If no suitable service is available for the proxy, returns * null. @@ -84,7 +90,7 @@ public class ProxyLocationProvider extends AbstractLocationProvider { int nonOverlayPackageResId) { // safe to use direct executor since our locks are not acquired in a code path invoked by // our owning provider - super(DIRECT_EXECUTOR, null, null); + super(DIRECT_EXECUTOR, null, null, null); mContext = context; mServiceWatcher = new ServiceWatcher(context, action, this::onBind, @@ -94,22 +100,34 @@ public class ProxyLocationProvider extends AbstractLocationProvider { mRequest = ProviderRequest.EMPTY_REQUEST; } + private void updateLocationTagInfo(@NonNull BoundService boundService) { + if (boundService.metadata != null) { + final String tagsList = boundService.metadata.getString(KEY_LOCATION_TAGS); + if (tagsList != null) { + final String[] tags = tagsList.split(LOCATION_TAGS_SEPARATOR); + setLocationTags(new ArraySet<>(tags)); + } + } + } + private boolean checkServiceResolves() { return mServiceWatcher.checkServiceResolves(); } - private void onBind(IBinder binder, ComponentName service) throws RemoteException { + private void onBind(IBinder binder, BoundService boundService) throws RemoteException { ILocationProvider provider = ILocationProvider.Stub.asInterface(binder); synchronized (mLock) { mProxy = new Proxy(); - mService = service; + mService = boundService.component; provider.setLocationProviderManager(mProxy); ProviderRequest request = mRequest; if (!request.equals(ProviderRequest.EMPTY_REQUEST)) { provider.setRequest(request); } + + updateLocationTagInfo(boundService); } } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index ecf4438d8aca..2b9dd2d84ac6 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -882,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 diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java index 6aefe41891f9..557fa8944445 100644 --- a/services/core/java/com/android/server/net/NetworkStatsCollection.java +++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java @@ -54,6 +54,8 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.FastDataInput; +import com.android.internal.util.FastDataOutput; import com.android.internal.util.FileRotator; import com.android.internal.util.IndentingPrintWriter; @@ -89,6 +91,9 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W /** File header magic number: "ANET" */ private static final int FILE_MAGIC = 0x414E4554; + /** Default buffer size from BufferedInputStream */ + private static final int BUFFER_SIZE = 8192; + private static final int VERSION_NETWORK_INIT = 1; private static final int VERSION_UID_INIT = 1; @@ -434,7 +439,8 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W @Override public void read(InputStream in) throws IOException { - read((DataInput) new DataInputStream(in)); + final FastDataInput dataIn = new FastDataInput(in, BUFFER_SIZE); + read(dataIn); } private void read(DataInput in) throws IOException { @@ -473,8 +479,9 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W @Override public void write(OutputStream out) throws IOException { - write((DataOutput) new DataOutputStream(out)); - out.flush(); + final FastDataOutput dataOut = new FastDataOutput(out, BUFFER_SIZE); + write(dataOut); + dataOut.flush(); } private void write(DataOutput out) throws IOException { 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/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 380cdb10569b..6875b8a5abeb 100644 --- a/services/core/java/com/android/server/pm/DumpState.java +++ b/services/core/java/com/android/server/pm/DumpState.java @@ -56,6 +56,7 @@ public final class DumpState { private boolean mTitlePrinted; private boolean mFullPreferred; + private boolean mCheckIn; private String mTargetPackageName; @@ -118,4 +119,12 @@ public final class DumpState { public void setFullPreferred(boolean fullPreferred) { mFullPreferred = fullPreferred; } + + public boolean isCheckIn() { + return mCheckIn; + } + + public void setCheckIn(boolean checkIn) { + mCheckIn = checkIn; + } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 281283048a95..a5e28f164bc2 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -530,14 +530,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements throw new SecurityException("User restriction prevents installing"); } - if (params.dataLoaderParams != null - && mContext.checkCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("You need the " - + "com.android.permission.USE_INSTALLER_V2 permission " - + "to use a data loader"); - } - // INSTALL_REASON_ROLLBACK allows an app to be rolled back without requiring the ROLLBACK // capability; ensure if this is set as the install reason the app has one of the necessary // signature permissions to perform the rollback. diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 7bf3c5c1f4c9..460b2f2bf5c6 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -3547,14 +3547,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public DataLoaderParamsParcel getDataLoaderParams() { - mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null); return params.dataLoaderParams != null ? params.dataLoaderParams.getData() : null; } @Override public void addFile(int location, String name, long lengthBytes, byte[] metadata, byte[] signature) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null); if (!isDataLoaderInstallation()) { throw new IllegalStateException( "Cannot add files to non-data loader installation session."); @@ -3587,7 +3585,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public void removeFile(int location, String name) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null); if (!isDataLoaderInstallation()) { throw new IllegalStateException( "Cannot add files to non-data loader installation session."); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 16966d4de4e6..8123951eadff 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1961,9 +1961,10 @@ public class PackageManagerService extends IPackageManager.Stub boolean isInstantAppInternal(String packageName, @UserIdInt int userId, int callingUid); boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId, int callingUid); boolean isInstantAppResolutionAllowed(Intent intent, List<ResolveInfo> resolvedActivities, - int userId, boolean skipPackageCheck); + int userId, boolean skipPackageCheck, int flags); boolean isInstantAppResolutionAllowedBody(Intent intent, - List<ResolveInfo> resolvedActivities, int userId, boolean skipPackageCheck); + List<ResolveInfo> resolvedActivities, int userId, boolean skipPackageCheck, + int flags); boolean isPersistentPreferredActivitySetByDpm(Intent intent, int userId, String resolvedType, int flags); boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId); @@ -2322,7 +2323,7 @@ public class PackageManagerService extends IPackageManager.Stub result = filterIfNotSystemUser(mComponentResolver.queryActivities( intent, resolvedType, flags, userId), userId); addInstant = isInstantAppResolutionAllowed(intent, result, userId, - false /*skipPackageCheck*/); + false /*skipPackageCheck*/, flags); // Check for cross profile results. boolean hasNonNegativePriorityResult = hasNonNegativePriority(result); xpResolveInfo = queryCrossProfileIntents( @@ -2387,8 +2388,8 @@ public class PackageManagerService extends IPackageManager.Stub if (result == null || result.size() == 0) { // the caller wants to resolve for a particular package; however, there // were no installed results, so, try to find an ephemeral result - addInstant = isInstantAppResolutionAllowed( - intent, null /*result*/, userId, true /*skipPackageCheck*/); + addInstant = isInstantAppResolutionAllowed(intent, null /*result*/, userId, + true /*skipPackageCheck*/, flags); if (result == null) { result = new ArrayList<>(); } @@ -2618,7 +2619,7 @@ public class PackageManagerService extends IPackageManager.Stub // We'll want to include browser possibilities in a few cases boolean includeBrowser = false; - if (!DomainVerificationUtils.isDomainVerificationIntent(intent)) { + if (!DomainVerificationUtils.isDomainVerificationIntent(intent, matchFlags)) { result.addAll(undefinedList); // Maybe add one for the other profile. if (xpDomainInfo != null && xpDomainInfo.highestApprovalLevel @@ -2802,7 +2803,7 @@ public class PackageManagerService extends IPackageManager.Stub } result.highestApprovalLevel = Math.max(mDomainVerificationManager - .approvalLevelForDomain(ps, intent, riTargetUser.targetUserId), + .approvalLevelForDomain(ps, intent, flags, riTargetUser.targetUserId), result.highestApprovalLevel); } if (result != null && result.highestApprovalLevel @@ -3049,7 +3050,7 @@ public class PackageManagerService extends IPackageManager.Stub final String packageName = info.activityInfo.packageName; final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps.getInstantApp(userId)) { - if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent, + if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent, flags, userId)) { if (DEBUG_INSTANT) { Slog.v(TAG, "Instant app approved for intent; pkg: " @@ -3928,7 +3929,7 @@ public class PackageManagerService extends IPackageManager.Stub public boolean isInstantAppResolutionAllowed( Intent intent, List<ResolveInfo> resolvedActivities, int userId, - boolean skipPackageCheck) { + boolean skipPackageCheck, int flags) { if (mInstantAppResolverConnection == null) { return false; } @@ -3961,14 +3962,14 @@ public class PackageManagerService extends IPackageManager.Stub // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution. // Or if there's already an ephemeral app installed that handles the action return isInstantAppResolutionAllowedBody(intent, resolvedActivities, userId, - skipPackageCheck); + skipPackageCheck, flags); } // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution. // Or if there's already an ephemeral app installed that handles the action public boolean isInstantAppResolutionAllowedBody( Intent intent, List<ResolveInfo> resolvedActivities, int userId, - boolean skipPackageCheck) { + boolean skipPackageCheck, int flags) { final int count = (resolvedActivities == null ? 0 : resolvedActivities.size()); for (int n = 0; n < count; n++) { final ResolveInfo info = resolvedActivities.get(n); @@ -3977,7 +3978,7 @@ public class PackageManagerService extends IPackageManager.Stub if (ps != null) { // only check domain verification status if the app is not a browser if (!info.handleAllWebDataURI) { - if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent, + if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent, flags, userId)) { if (DEBUG_INSTANT) { Slog.v(TAG, "DENY instant app;" + " pkg: " + packageName @@ -4403,6 +4404,7 @@ public class PackageManagerService extends IPackageManager.Stub public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) { final String packageName = dumpState.getTargetPackageName(); + final boolean checkin = dumpState.isCheckIn(); switch (type) { case DumpState.DUMP_VERSION: @@ -4415,6 +4417,56 @@ public class PackageManagerService extends IPackageManager.Stub break; } + case DumpState.DUMP_LIBS: + { + boolean printedHeader = false; + final int numSharedLibraries = mSharedLibraries.size(); + for (int index = 0; index < numSharedLibraries; index++) { + final String libName = mSharedLibraries.keyAt(index); + final 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(); + } + } + break; + } + case DumpState.DUMP_PREFERRED_XML: { pw.flush(); @@ -4676,10 +4728,11 @@ public class PackageManagerService extends IPackageManager.Stub } } public boolean isInstantAppResolutionAllowedBody(Intent intent, - List<ResolveInfo> resolvedActivities, int userId, boolean skipPackageCheck) { + List<ResolveInfo> resolvedActivities, int userId, boolean skipPackageCheck, + int flags) { synchronized (mLock) { return super.isInstantAppResolutionAllowedBody(intent, resolvedActivities, userId, - skipPackageCheck); + skipPackageCheck, flags); } } public int getPackageUidInternal(String packageName, int flags, int userId, @@ -9167,6 +9220,11 @@ public class PackageManagerService extends IPackageManager.Stub */ @Override public String[] getPackagesForUid(int uid) { + final int callingUid = Binder.getCallingUid(); + final int userId = UserHandle.getUserId(uid); + enforceCrossUserOrProfilePermission(callingUid, userId, + /* requireFullPermission */ false, + /* checkShell */ false, "getPackagesForUid"); return snapshotComputer().getPackagesForUid(uid); } @@ -9470,20 +9528,20 @@ public class PackageManagerService extends IPackageManager.Stub private boolean isInstantAppResolutionAllowed( Intent intent, List<ResolveInfo> resolvedActivities, int userId, - boolean skipPackageCheck) { + boolean skipPackageCheck, int flags) { return liveComputer().isInstantAppResolutionAllowed( intent, resolvedActivities, userId, - skipPackageCheck); + skipPackageCheck, flags); } // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution. // Or if there's already an ephemeral app installed that handles the action private boolean isInstantAppResolutionAllowedBody( Intent intent, List<ResolveInfo> resolvedActivities, int userId, - boolean skipPackageCheck) { + boolean skipPackageCheck, int flags) { return liveComputer().isInstantAppResolutionAllowedBody( intent, resolvedActivities, userId, - skipPackageCheck); + skipPackageCheck, flags); } private void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj, @@ -9540,7 +9598,7 @@ public class PackageManagerService extends IPackageManager.Stub final String packageName = ri.activityInfo.packageName; final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null && hasAnyDomainApproval(mDomainVerificationManager, ps, - intent, userId)) { + intent, flags, userId)) { return ri; } } @@ -9597,8 +9655,9 @@ public class PackageManagerService extends IPackageManager.Stub */ private static boolean hasAnyDomainApproval( @NonNull DomainVerificationManagerInternal manager, @NonNull PackageSetting pkgSetting, - @NonNull Intent intent, @UserIdInt int userId) { - return manager.approvalLevelForDomain(pkgSetting, intent, userId) + @NonNull Intent intent, @PackageManager.ResolveInfoFlags int resolveInfoFlags, + @UserIdInt int userId) { + return manager.approvalLevelForDomain(pkgSetting, intent, resolveInfoFlags, userId) > DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE; } @@ -23699,8 +23758,6 @@ public class PackageManagerService extends IPackageManager.Stub if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return; DumpState dumpState = new DumpState(); - boolean checkin = false; - ArraySet<String> permissionNames = null; int opti = 0; @@ -23750,7 +23807,7 @@ public class PackageManagerService extends IPackageManager.Stub pw.println(" <package.name>: info about given package"); return; } else if ("--checkin".equals(opt)) { - checkin = true; + dumpState.setCheckIn(true); } else if ("--all-components".equals(opt)) { dumpState.setOptionEnabled(DumpState.OPTION_DUMP_ALL_COMPONENTS); } else if ("-f".equals(opt)) { @@ -23904,6 +23961,7 @@ public class PackageManagerService extends IPackageManager.Stub } final String packageName = dumpState.getTargetPackageName(); + final boolean checkin = dumpState.isCheckIn(); if (checkin) { pw.println("vers,1"); } @@ -23992,11 +24050,7 @@ public class PackageManagerService extends IPackageManager.Stub } 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); - } + dump(DumpState.DUMP_LIBS, fd, pw, dumpState); } if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) { @@ -24337,53 +24391,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - 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; - } - 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(); - } - } - } - // ------- apps on sdcard specific code ------- static final boolean DEBUG_SD_INSTALL = false; diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 24c27bedb9f7..c75dd27a383a 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -983,7 +983,11 @@ public class UserManagerService extends IUserManager.Stub { @Override public UserInfo getProfileParent(@UserIdInt int userId) { - checkManageUsersPermission("get the profile parent"); + if (!hasManageUsersOrPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)) { + throw new SecurityException( + "You need MANAGE_USERS or INTERACT_ACROSS_USERS permission to get the " + + "profile parent"); + } synchronized (mUsersLock) { return getProfileParentLU(userId); } @@ -1531,11 +1535,14 @@ public class UserManagerService extends IUserManager.Stub { @Override public String getUserName() { - if (!hasManageUsersOrPermission(android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED)) { - throw new SecurityException("You need MANAGE_USERS or GET_ACCOUNTS_PRIVILEGED " - + "permissions to: get user name"); + final int callingUid = Binder.getCallingUid(); + if (!hasManageOrCreateUsersPermission() + || hasPermissionGranted( + android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED, callingUid)) { + throw new SecurityException("You need MANAGE_USERS or CREATE_USERS or " + + "GET_ACCOUNTS_PRIVILEGED permissions to: get user name"); } - final int userId = UserHandle.getUserId(Binder.getCallingUid()); + final int userId = UserHandle.getUserId(callingUid); synchronized (mUsersLock) { UserInfo userInfo = userWithName(getUserInfoLU(userId)); return userInfo == null ? "" : userInfo.name; @@ -3287,7 +3294,7 @@ public class UserManagerService extends IUserManager.Stub { * as well as for {@link UserManager#USER_TYPE_FULL_RESTRICTED}. */ @Override - public UserInfo createProfileForUserWithThrow(String name, @NonNull String userType, + public UserInfo createProfileForUserWithThrow(@Nullable String name, @NonNull String userType, @UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages) throws ServiceSpecificException { checkManageOrCreateUsersPermission(flags); @@ -3868,7 +3875,7 @@ public class UserManagerService extends IUserManager.Stub { * @hide */ @Override - public UserInfo createRestrictedProfileWithThrow(String name, int parentUserId) { + public UserInfo createRestrictedProfileWithThrow(@Nullable String name, int parentUserId) { checkManageOrCreateUsersPermission("setupRestrictedProfile"); final UserInfo user = createProfileForUserWithThrow( name, UserManager.USER_TYPE_FULL_RESTRICTED, 0, parentUserId, null); diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java index b6ea901ecd6b..9e22d82910df 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java @@ -310,7 +310,7 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan */ @ApprovalLevel int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent, - @UserIdInt int userId); + @PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId); /** * @return the domain verification set ID for the given package, or null if the ID is diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java index 8e5aead05f96..8dcb45ff5ab6 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java @@ -1346,9 +1346,9 @@ public class DomainVerificationService extends SystemService @Override public int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent, - @UserIdInt int userId) { + @PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId) { String packageName = pkgSetting.name; - if (!DomainVerificationUtils.isDomainVerificationIntent(intent)) { + if (!DomainVerificationUtils.isDomainVerificationIntent(intent, resolveInfoFlags)) { if (DEBUG_APPROVAL) { debugApproval(packageName, intent, userId, false, "not valid intent"); } diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java index 475d3a87b427..783aff6ccb55 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java @@ -40,10 +40,13 @@ public final class DomainVerificationUtils { throw new NameNotFoundException("Package " + packageName + " unavailable"); } - public static boolean isDomainVerificationIntent(Intent intent) { - return intent.isWebIntent() - && intent.hasCategory(Intent.CATEGORY_BROWSABLE) - && intent.hasCategory(Intent.CATEGORY_DEFAULT); + public static boolean isDomainVerificationIntent(Intent intent, int resolveInfoFlags) { + if (!intent.isWebIntent() || !intent.hasCategory(Intent.CATEGORY_BROWSABLE)) { + return false; + } + + return ((resolveInfoFlags & PackageManager.MATCH_DEFAULT_ONLY) != 0) + || intent.hasCategory(Intent.CATEGORY_DEFAULT); } static boolean isChangeEnabled(PlatformCompat platformCompat, AndroidPackage pkg, diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java new file mode 100644 index 000000000000..c9653909adb6 --- /dev/null +++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.policy; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.AppOpsManager; +import android.app.AppOpsManagerInternal; +import android.location.LocationManagerInternal; +import android.util.ArrayMap; +import android.util.ArraySet; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.function.HeptFunction; +import com.android.internal.util.function.QuadFunction; +import com.android.server.LocalServices; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * This class defines policy for special behaviors around app ops. + */ +public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegate { + @NonNull + private final Object mLock = new Object(); + + /** + * The locking policy around the location tags is a bit special. Since we want to + * avoid grabbing the lock on every op note we are taking the approach where the + * read and write are being done via a thread-safe data structure such that the + * lookup/insert are single thread-safe calls. When we update the cached state we + * use a lock to ensure the update's lookup and store calls are done atomically, + * so multiple writers would not interleave. The tradeoff is we make is that the + * concurrent data structure would use boxing/unboxing of integers but this is + * preferred to locking. + */ + @GuardedBy("mLock - writes only - see above") + @NonNull + private final ConcurrentHashMap<Integer, ArrayMap<String, ArraySet<String>>> mLocationTags = + new ConcurrentHashMap(); + + public AppOpsPolicy() { + final LocationManagerInternal locationManagerInternal = LocalServices.getService( + LocationManagerInternal.class); + locationManagerInternal.setOnProviderLocationTagsChangeListener((providerTagInfo) -> { + synchronized (mLock) { + final int uid = providerTagInfo.getUid(); + // We make a copy of the per UID state to limit our mutation to one + // operation in the underlying concurrent data structure. + ArrayMap<String, ArraySet<String>> uidTags = mLocationTags.get(uid); + if (uidTags != null) { + uidTags = new ArrayMap<>(uidTags); + } + + final String packageName = providerTagInfo.getPackageName(); + ArraySet<String> packageTags = (uidTags != null) ? uidTags.get(packageName) : null; + if (packageTags != null) { + packageTags = new ArraySet<>(packageTags); + } + + final Set<String> providerTags = providerTagInfo.getTags(); + if (providerTags != null && !providerTags.isEmpty()) { + if (packageTags != null) { + packageTags.clear(); + packageTags.addAll(providerTags); + } else { + packageTags = new ArraySet<>(providerTags); + } + if (uidTags == null) { + uidTags = new ArrayMap<>(); + } + uidTags.put(packageName, packageTags); + mLocationTags.put(uid, uidTags); + } else if (uidTags != null) { + uidTags.remove(packageName); + if (!uidTags.isEmpty()) { + mLocationTags.put(uid, uidTags); + } else { + mLocationTags.remove(uid); + } + } + } + }); + } + + @Override + public int checkOperation(int code, int uid, String packageName, boolean raw, + QuadFunction<Integer, Integer, String, Boolean, Integer> superImpl) { + return superImpl.apply(code, uid, packageName, raw); + } + + @Override + public int checkAudioOperation(int code, int usage, int uid, String packageName, + QuadFunction<Integer, Integer, Integer, String, Integer> superImpl) { + return superImpl.apply(code, usage, uid, packageName); + } + + @Override + public int noteOperation(int code, int uid, @Nullable String packageName, + @Nullable String featureId, boolean shouldCollectAsyncNotedOp, @Nullable String message, + boolean shouldCollectMessage, @NonNull HeptFunction<Integer, Integer, String, String, + Boolean, String, Boolean, Integer> superImpl) { + if (isHandledOp(code)) { + // Only a single lookup from the underlying concurrent data structure + final ArrayMap<String, ArraySet<String>> uidTags = mLocationTags.get(uid); + if (uidTags != null) { + final ArraySet<String> packageTags = uidTags.get(packageName); + if (packageTags != null && packageTags.contains(featureId)) { + return superImpl.apply(resolveLocationOp(code), uid, packageName, featureId, + shouldCollectAsyncNotedOp, message, shouldCollectMessage); + } + } + } + return superImpl.apply(code, uid, packageName, featureId, shouldCollectAsyncNotedOp, + message, shouldCollectMessage); + } + + private static boolean isHandledOp(int code) { + switch (code) { + case AppOpsManager.OP_FINE_LOCATION: + case AppOpsManager.OP_COARSE_LOCATION: + return true; + } + return false; + } + + private static int resolveLocationOp(int code) { + switch (code) { + case AppOpsManager.OP_FINE_LOCATION: + return AppOpsManager.OP_FINE_LOCATION_SOURCE; + case AppOpsManager.OP_COARSE_LOCATION: + return AppOpsManager.OP_COARSE_LOCATION_SOURCE; + } + return code; + } +} diff --git a/services/core/java/com/android/server/policy/KeyCombinationManager.java b/services/core/java/com/android/server/policy/KeyCombinationManager.java index 84ac12497e71..7f55723cda0b 100644 --- a/services/core/java/com/android/server/policy/KeyCombinationManager.java +++ b/services/core/java/com/android/server/policy/KeyCombinationManager.java @@ -102,9 +102,11 @@ public class KeyCombinationManager { } /** - * Check if the key event could be triggered by combine key rule before dispatching to a window. + * Check if the key event could be intercepted by combination key rule before it is dispatched + * to a window. + * Return true if any active rule could be triggered by the key event, otherwise false. */ - void interceptKey(KeyEvent event, boolean interactive) { + boolean interceptKey(KeyEvent event, boolean interactive) { final boolean down = event.getAction() == KeyEvent.ACTION_DOWN; final int keyCode = event.getKeyCode(); final int count = mActiveRules.size(); @@ -117,9 +119,9 @@ public class KeyCombinationManager { // exceed time from first key down. forAllRules(mActiveRules, (rule)-> rule.cancel()); mActiveRules.clear(); - return; + return false; } else if (count == 0) { // has some key down but no active rule exist. - return; + return false; } } @@ -127,7 +129,7 @@ public class KeyCombinationManager { mDownTimes.put(keyCode, eventTime); } else { // ignore old key, maybe a repeat key. - return; + return false; } if (mDownTimes.size() == 1) { @@ -141,7 +143,7 @@ public class KeyCombinationManager { } else { // Ignore if rule already triggered. if (mTriggeredRule != null) { - return; + return true; } // check if second key can trigger rule, or remove the non-match rule. @@ -156,6 +158,7 @@ public class KeyCombinationManager { mActiveRules.clear(); if (mTriggeredRule != null) { mActiveRules.add(mTriggeredRule); + return true; } } } else { @@ -168,6 +171,7 @@ public class KeyCombinationManager { } } } + return false; } /** diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 4ccd57ddc977..1b192e43c7b8 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -73,6 +73,8 @@ import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY; +import static com.android.server.policy.SingleKeyGestureDetector.KEY_LONGPRESS; +import static com.android.server.policy.SingleKeyGestureDetector.KEY_VERYLONGPRESS; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED; @@ -456,7 +458,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { volatile boolean mPowerKeyHandled; volatile boolean mBackKeyHandled; volatile boolean mBeganFromNonInteractive; - volatile int mPowerKeyPressCounter; volatile boolean mEndCallKeyHandled; volatile boolean mCameraGestureTriggeredDuringGoingToSleep; volatile boolean mGoingToSleep; @@ -497,7 +498,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean mHasSoftInput = false; boolean mHapticTextHandleEnabled; boolean mUseTvRouting; - int mVeryLongPressTimeout; boolean mAllowStartActivityForLongPressOnPowerDuringSetup; MetricsLogger mLogger; boolean mWakeOnDpadKeyPress; @@ -520,8 +520,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean mConsumeSearchKeyUp; boolean mPendingMetaAction; boolean mPendingCapsLockToggle; - int mMetaState; - int mInitialMetaState; // support for activating the lock screen while the screen is on private HashSet<Integer> mAllowLockscreenWhenOnDisplays = new HashSet<>(); @@ -597,14 +595,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { private final com.android.internal.policy.LogDecelerateInterpolator mLogDecelerateInterpolator = new LogDecelerateInterpolator(100, 0); - private final MutableBoolean mTmpBoolean = new MutableBoolean(false); - private boolean mPerDisplayFocusEnabled = false; private volatile int mTopFocusedDisplayId = INVALID_DISPLAY; private int mPowerButtonSuppressionDelayMillis = POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS; private KeyCombinationManager mKeyCombinationManager; + private SingleKeyGestureDetector mSingleKeyGestureDetector; private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3; private static final int MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK = 4; @@ -615,10 +612,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private static final int MSG_DISPATCH_SHOW_GLOBAL_ACTIONS = 10; private static final int MSG_HIDE_BOOT_MESSAGE = 11; private static final int MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK = 12; - private static final int MSG_POWER_DELAYED_PRESS = 13; - private static final int MSG_POWER_LONG_PRESS = 14; private static final int MSG_SHOW_PICTURE_IN_PICTURE_MENU = 15; - private static final int MSG_BACK_LONG_PRESS = 16; private static final int MSG_ACCESSIBILITY_SHORTCUT = 17; private static final int MSG_BUGREPORT_TV = 18; private static final int MSG_ACCESSIBILITY_TV = 19; @@ -626,8 +620,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private static final int MSG_SYSTEM_KEY_PRESS = 21; private static final int MSG_HANDLE_ALL_APPS = 22; private static final int MSG_LAUNCH_ASSIST = 23; - private static final int MSG_POWER_VERY_LONG_PRESS = 25; - private static final int MSG_RINGER_TOGGLE_CHORD = 26; + private static final int MSG_RINGER_TOGGLE_CHORD = 24; private class PolicyHandler extends Handler { @Override @@ -668,22 +661,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { case MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK: launchVoiceAssistWithWakeLock(); break; - case MSG_POWER_DELAYED_PRESS: - powerPress((Long) msg.obj, msg.arg1 != 0, msg.arg2); - finishPowerKeyPress(); - break; - case MSG_POWER_LONG_PRESS: - powerLongPress((Long) msg.obj /* eventTime */); - break; - case MSG_POWER_VERY_LONG_PRESS: - powerVeryLongPress(); - break; case MSG_SHOW_PICTURE_IN_PICTURE_MENU: showPictureInPictureMenuInternal(); break; - case MSG_BACK_LONG_PRESS: - backLongPress(); - break; case MSG_ACCESSIBILITY_SHORTCUT: accessibilityShortcutActivated(); break; @@ -794,13 +774,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } }; - private Runnable mPossibleVeryLongPressReboot = new Runnable() { - @Override - public void run() { - mActivityManagerInternal.prepareForPossibleShutdown(); - } - }; - private void handleRingerChordGesture() { if (mRingerToggleChord == VOLUME_HUSH_OFF) { return; @@ -840,28 +813,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - private void interceptBackKeyDown() { - mLogger.count("key_back_down", 1); - // Reset back key state for long press - mBackKeyHandled = false; - - if (hasLongPressOnBackBehavior()) { - Message msg = mHandler.obtainMessage(MSG_BACK_LONG_PRESS); - msg.setAsynchronous(true); - mHandler.sendMessageDelayed(msg, - ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout()); - } - } // returns true if the key was handled and should not be passed to the user - private boolean interceptBackKeyUp(KeyEvent event) { - mLogger.count("key_back_up", 1); + private boolean backKeyPress() { + mLogger.count("key_back_press", 1); // Cache handled state boolean handled = mBackKeyHandled; - // Reset back long press state - cancelPendingBackKeyAction(); - if (mHasFeatureWatch) { TelecomManager telecomManager = getTelecommService(); @@ -883,10 +841,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - if (mAutofillManagerInternal != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK) { + if (mAutofillManagerInternal != null) { mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_BACK_KEY_TO_AUTOFILL)); } - return handled; } @@ -896,11 +853,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { mPowerKeyWakeLock.acquire(); } - // Cancel multi-press detection timeout. - if (mPowerKeyPressCounter != 0) { - mHandler.removeMessages(MSG_POWER_DELAYED_PRESS); - } - mWindowManagerFuncs.onPowerKeyDown(interactive); // Stop ringing or end call if configured to do so when power is pressed. @@ -922,71 +874,20 @@ public class PhoneWindowManager implements WindowManagerPolicy { final boolean handledByPowerManager = mPowerManagerInternal.interceptPowerKeyDown(event); - GestureLauncherService gestureService = LocalServices.getService( - GestureLauncherService.class); - boolean gesturedServiceIntercepted = false; - if (gestureService != null) { - gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive, - mTmpBoolean); - if (mTmpBoolean.value && mRequestedOrGoingToSleep) { - mCameraGestureTriggeredDuringGoingToSleep = true; - } - } - // Inform the StatusBar; but do not allow it to consume the event. sendSystemKeyToStatusBarAsync(event.getKeyCode()); - schedulePossibleVeryLongPressReboot(); - // If the power key has still not yet been handled, then detect short // press, long press, or multi press and decide what to do. - mPowerKeyHandled = hungUp || gesturedServiceIntercepted + mPowerKeyHandled = mPowerKeyHandled || hungUp || handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted(); if (!mPowerKeyHandled) { - if (interactive) { - // When interactive, we're already awake. - // Wait for a long press or for the button to be released to decide what to do. - if (hasLongPressOnPowerBehavior()) { - if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) { - powerLongPress(event.getEventTime()); - } else { - Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS, - event.getEventTime()); - msg.setAsynchronous(true); - mHandler.sendMessageDelayed(msg, - ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout()); - - if (hasVeryLongPressOnPowerBehavior()) { - Message longMsg = mHandler.obtainMessage(MSG_POWER_VERY_LONG_PRESS); - longMsg.setAsynchronous(true); - mHandler.sendMessageDelayed(longMsg, mVeryLongPressTimeout); - } - } - } - } else { + if (!interactive) { wakeUpFromPowerKey(event.getDownTime()); - if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) { - if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) { - powerLongPress(event.getEventTime()); - } else { - Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS, - event.getEventTime()); - msg.setAsynchronous(true); - mHandler.sendMessageDelayed(msg, - ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout()); - - if (hasVeryLongPressOnPowerBehavior()) { - Message longMsg = mHandler.obtainMessage(MSG_POWER_VERY_LONG_PRESS); - longMsg.setAsynchronous(true); - mHandler.sendMessageDelayed(longMsg, mVeryLongPressTimeout); - } - } - mBeganFromNonInteractive = true; } else { final int maxCount = getMaxMultiPressPowerCount(); - if (maxCount <= 1) { mPowerKeyHandled = true; } else { @@ -994,68 +895,38 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } } + } else { + // handled by another power key policy. + if (!mSingleKeyGestureDetector.isKeyIntercepted(KEYCODE_POWER)) { + mSingleKeyGestureDetector.reset(); + } } } private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) { final boolean handled = canceled || mPowerKeyHandled; - cancelPendingPowerKeyAction(); if (!handled) { if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) == 0) { // Abort possibly stuck animations only when power key up without long press case. mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe); } - - // Figure out how to handle the key now that it has been released. - mPowerKeyPressCounter += 1; - - final int maxCount = getMaxMultiPressPowerCount(); - final long eventTime = event.getDownTime(); - if (mPowerKeyPressCounter < maxCount) { - // This could be a multi-press. Wait a little bit longer to confirm. - // Continue holding the wake lock. - Message msg = mHandler.obtainMessage(MSG_POWER_DELAYED_PRESS, - interactive ? 1 : 0, mPowerKeyPressCounter, eventTime); - msg.setAsynchronous(true); - mHandler.sendMessageDelayed(msg, ViewConfiguration.getMultiPressTimeout()); - return; - } - - // No other actions. Handle it immediately. - powerPress(eventTime, interactive, mPowerKeyPressCounter); + } else { + // handled by single key or another power key policy. + mSingleKeyGestureDetector.reset(); + finishPowerKeyPress(); } - // Done. Reset our state. - finishPowerKeyPress(); } private void finishPowerKeyPress() { mBeganFromNonInteractive = false; - mPowerKeyPressCounter = 0; + mPowerKeyHandled = false; if (mPowerKeyWakeLock.isHeld()) { mPowerKeyWakeLock.release(); } } - private void cancelPendingPowerKeyAction() { - if (!mPowerKeyHandled) { - mPowerKeyHandled = true; - mHandler.removeMessages(MSG_POWER_LONG_PRESS); - } - if (hasVeryLongPressOnPowerBehavior()) { - mHandler.removeMessages(MSG_POWER_VERY_LONG_PRESS); - } - cancelPossibleVeryLongPressReboot(); - } - - private void cancelPendingBackKeyAction() { - if (!mBackKeyHandled) { - mBackKeyHandled = true; - mHandler.removeMessages(MSG_BACK_LONG_PRESS); - } - } - private void powerPress(long eventTime, boolean interactive, int count) { if (mDefaultDisplayPolicy.isScreenOnEarly() && !mDefaultDisplayPolicy.isScreenOnFully()) { Slog.i(TAG, "Suppressed redundant power key press while " @@ -1206,6 +1077,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private void powerLongPress(long eventTime) { final int behavior = getResolvedLongPressOnPowerBehavior(); + switch (behavior) { case LONG_PRESS_POWER_NOTHING: break; @@ -1844,8 +1716,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { com.android.internal.R.integer.config_triplePressOnPowerBehavior); mShortPressOnSleepBehavior = mContext.getResources().getInteger( com.android.internal.R.integer.config_shortPressOnSleepBehavior); - mVeryLongPressTimeout = mContext.getResources().getInteger( - com.android.internal.R.integer.config_veryLongPressTimeout); mAllowStartActivityForLongPressOnPowerDuringSetup = mContext.getResources().getBoolean( com.android.internal.R.bool.config_allowStartActivityForLongPressOnPowerInSetup); @@ -1939,6 +1809,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } }); initKeyCombinationRules(); + initSingleKeyGestureRules(); } private void initKeyCombinationRules() { @@ -1951,7 +1822,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_POWER) { @Override void execute() { - cancelPendingPowerKeyAction(); + mPowerKeyHandled = true; interceptScreenshotChord(); } @Override @@ -1986,7 +1857,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override void execute() { - cancelPendingPowerKeyAction(); + mPowerKeyHandled = true; interceptRingerToggleChord(); } @Override @@ -2000,7 +1871,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { new TwoKeysCombinationRule(KEYCODE_BACK, KEYCODE_DPAD_DOWN) { @Override void execute() { - cancelPendingBackKeyAction(); + mBackKeyHandled = true; interceptAccessibilityGestureTv(); } @@ -2014,7 +1885,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { new TwoKeysCombinationRule(KEYCODE_DPAD_CENTER, KEYCODE_BACK) { @Override void execute() { - cancelPendingBackKeyAction(); + mBackKeyHandled = true; interceptBugreportGestureTv(); } @@ -2027,6 +1898,84 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** + * Rule for single power key gesture. + */ + private final class PowerKeyRule extends SingleKeyGestureDetector.SingleKeyRule { + PowerKeyRule(int gestures) { + super(KEYCODE_POWER, gestures); + } + + @Override + int getMaxMultiPressCount() { + return getMaxMultiPressPowerCount(); + } + + @Override + void onPress(long downTime) { + powerPress(downTime, true, 1 /*count*/); + finishPowerKeyPress(); + } + + @Override + void onLongPress(long downTime) { + powerLongPress(downTime); + } + + @Override + void onVeryLongPress(long downTime) { + mActivityManagerInternal.prepareForPossibleShutdown(); + powerVeryLongPress(); + } + + @Override + void onMultiPress(long downTime, int count) { + powerPress(downTime, true, count); + finishPowerKeyPress(); + } + } + + /** + * Rule for single back key gesture. + */ + private final class BackKeyRule extends SingleKeyGestureDetector.SingleKeyRule { + BackKeyRule(int gestures) { + super(KEYCODE_BACK, gestures); + } + + @Override + int getMaxMultiPressCount() { + return 1; + } + + @Override + void onPress(long downTime) { + mBackKeyHandled |= backKeyPress(); + } + + @Override + void onLongPress(long downTime) { + backLongPress(); + } + } + + private void initSingleKeyGestureRules() { + mSingleKeyGestureDetector = new SingleKeyGestureDetector(mContext); + + int powerKeyGestures = 0; + if (hasVeryLongPressOnPowerBehavior()) { + powerKeyGestures |= KEY_VERYLONGPRESS; + } + if (hasLongPressOnPowerBehavior()) { + powerKeyGestures |= KEY_LONGPRESS; + } + mSingleKeyGestureDetector.addRule(new PowerKeyRule(powerKeyGestures)); + + if (hasLongPressOnBackBehavior()) { + mSingleKeyGestureDetector.addRule(new BackKeyRule(KEY_LONGPRESS)); + } + } + + /** * Read values from config.xml that may be overridden depending on * the configuration of the device. * eg. Disable long press on home goes to recents on sw600dp. @@ -2547,6 +2496,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { final boolean down = event.getAction() == KeyEvent.ACTION_DOWN; final boolean canceled = event.isCanceled(); final int displayId = event.getDisplayId(); + final long key_consumed = -1; if (DEBUG_INPUT) { Log.d(TAG, "interceptKeyTi keyCode=" + keyCode + " down=" + down + " repeatCount=" @@ -2554,7 +2504,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if (mKeyCombinationManager.isKeyConsumed(event)) { - return -1; + return key_consumed; } if ((flags & KeyEvent.FLAG_FALLBACK) == 0) { @@ -2575,205 +2525,250 @@ public class PhoneWindowManager implements WindowManagerPolicy { mPendingCapsLockToggle = false; } - // First we always handle the home key here, so applications - // can never break it, although if keyguard is on, we do let - // it handle it, because that gives us the correct 5 second - // timeout. - if (keyCode == KEYCODE_HOME) { - DisplayHomeButtonHandler handler = mDisplayHomeButtonHandlers.get(displayId); - if (handler == null) { - handler = new DisplayHomeButtonHandler(displayId); - mDisplayHomeButtonHandlers.put(displayId, handler); - } - return handler.handleHomeButton(focusedToken, event); - } else if (keyCode == KeyEvent.KEYCODE_MENU) { - // Hijack modified menu keys for debugging features - final int chordBug = KeyEvent.META_SHIFT_ON; - - if (down && repeatCount == 0) { - if (mEnableShiftMenuBugReports && (metaState & chordBug) == chordBug) { - Intent intent = new Intent(Intent.ACTION_BUG_REPORT); - mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, - null, null, null, 0, null, null); - return -1; + switch(keyCode) { + case KeyEvent.KEYCODE_HOME: + // First we always handle the home key here, so applications + // can never break it, although if keyguard is on, we do let + // it handle it, because that gives us the correct 5 second + // timeout. + DisplayHomeButtonHandler handler = mDisplayHomeButtonHandlers.get(displayId); + if (handler == null) { + handler = new DisplayHomeButtonHandler(displayId); + mDisplayHomeButtonHandlers.put(displayId, handler); } - } - } else if (keyCode == KeyEvent.KEYCODE_SEARCH) { - if (down) { - if (repeatCount == 0) { - mSearchKeyShortcutPending = true; - mConsumeSearchKeyUp = false; + return handler.handleHomeButton(focusedToken, event); + case KeyEvent.KEYCODE_MENU: + // Hijack modified menu keys for debugging features + final int chordBug = KeyEvent.META_SHIFT_ON; + + if (down && repeatCount == 0) { + if (mEnableShiftMenuBugReports && (metaState & chordBug) == chordBug) { + Intent intent = new Intent(Intent.ACTION_BUG_REPORT); + mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, + null, null, null, 0, null, null); + return key_consumed; + } } - } else { - mSearchKeyShortcutPending = false; - if (mConsumeSearchKeyUp) { - mConsumeSearchKeyUp = false; - return -1; + break; + case KeyEvent.KEYCODE_SEARCH: + if (down) { + if (repeatCount == 0) { + mSearchKeyShortcutPending = true; + mConsumeSearchKeyUp = false; + } + } else { + mSearchKeyShortcutPending = false; + if (mConsumeSearchKeyUp) { + mConsumeSearchKeyUp = false; + return key_consumed; + } } - } - return 0; - } else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) { - if (!keyguardOn) { - if (down && repeatCount == 0) { - preloadRecentApps(); - } else if (!down) { - toggleRecentApps(); + return 0; + case KeyEvent.KEYCODE_APP_SWITCH: + if (!keyguardOn) { + if (down && repeatCount == 0) { + preloadRecentApps(); + } else if (!down) { + toggleRecentApps(); + } } - } - return -1; - } else if (keyCode == KeyEvent.KEYCODE_N && event.isMetaPressed()) { - if (down) { - IStatusBarService service = getStatusBarService(); - if (service != null) { - try { - service.expandNotificationsPanel(); - } catch (RemoteException e) { - // do nothing. + return key_consumed; + case KeyEvent.KEYCODE_N: + if (down && event.isMetaPressed()) { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.expandNotificationsPanel(); + } catch (RemoteException e) { + // do nothing. + } + return key_consumed; } } - } - } else if (keyCode == KeyEvent.KEYCODE_S && event.isMetaPressed() - && event.isCtrlPressed()) { - if (down && repeatCount == 0) { - int type = event.isShiftPressed() ? TAKE_SCREENSHOT_SELECTED_REGION - : TAKE_SCREENSHOT_FULLSCREEN; - mScreenshotRunnable.setScreenshotType(type); - mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_OTHER); - mHandler.post(mScreenshotRunnable); - return -1; - } - } else if (keyCode == KeyEvent.KEYCODE_SLASH && event.isMetaPressed()) { - if (down && repeatCount == 0 && !isKeyguardLocked()) { - toggleKeyboardShortcutsMenu(event.getDeviceId()); - } - } else if (keyCode == KeyEvent.KEYCODE_ASSIST) { - Slog.wtf(TAG, "KEYCODE_ASSIST should be handled in interceptKeyBeforeQueueing"); - return -1; - } else if (keyCode == KeyEvent.KEYCODE_VOICE_ASSIST) { - Slog.wtf(TAG, "KEYCODE_VOICE_ASSIST should be handled in interceptKeyBeforeQueueing"); - return -1; - } else if (keyCode == KeyEvent.KEYCODE_SYSRQ) { - if (down && repeatCount == 0) { - mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN); - mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_OTHER); - mHandler.post(mScreenshotRunnable); - } - return -1; - } else if (keyCode == KeyEvent.KEYCODE_BRIGHTNESS_UP - || keyCode == KeyEvent.KEYCODE_BRIGHTNESS_DOWN) { - if (down) { - int direction = keyCode == KeyEvent.KEYCODE_BRIGHTNESS_UP ? 1 : -1; - - // Disable autobrightness if it's on - int auto = Settings.System.getIntForUser( - mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, - UserHandle.USER_CURRENT_OR_SELF); - if (auto != 0) { - Settings.System.putIntForUser(mContext.getContentResolver(), + break; + case KeyEvent.KEYCODE_S: + if (down && event.isMetaPressed() && event.isCtrlPressed() && repeatCount == 0) { + int type = event.isShiftPressed() ? TAKE_SCREENSHOT_SELECTED_REGION + : TAKE_SCREENSHOT_FULLSCREEN; + mScreenshotRunnable.setScreenshotType(type); + mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_OTHER); + mHandler.post(mScreenshotRunnable); + return key_consumed; + } + break; + case KeyEvent.KEYCODE_SLASH: + if (down && repeatCount == 0 && event.isMetaPressed() && !keyguardOn) { + toggleKeyboardShortcutsMenu(event.getDeviceId()); + return key_consumed; + } + break; + case KeyEvent.KEYCODE_ASSIST: + Slog.wtf(TAG, "KEYCODE_ASSIST should be handled in interceptKeyBeforeQueueing"); + return key_consumed; + case KeyEvent.KEYCODE_VOICE_ASSIST: + Slog.wtf(TAG, "KEYCODE_VOICE_ASSIST should be handled in" + + " interceptKeyBeforeQueueing"); + return key_consumed; + case KeyEvent.KEYCODE_SYSRQ: + if (down && repeatCount == 0) { + mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN); + mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_OTHER); + mHandler.post(mScreenshotRunnable); + } + return key_consumed; + case KeyEvent.KEYCODE_BRIGHTNESS_UP: + case KeyEvent.KEYCODE_BRIGHTNESS_DOWN: + if (down) { + int direction = keyCode == KeyEvent.KEYCODE_BRIGHTNESS_UP ? 1 : -1; + + // Disable autobrightness if it's on + int auto = Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT_OR_SELF); + if (auto != 0) { + Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, + UserHandle.USER_CURRENT_OR_SELF); + } + float minFloat = mPowerManager.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM); + float maxFloat = mPowerManager.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM); + float stepFloat = (maxFloat - minFloat) / BRIGHTNESS_STEPS * direction; + float brightnessFloat = Settings.System.getFloatForUser( + mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_FLOAT, + mContext.getDisplay().getBrightnessDefault(), + UserHandle.USER_CURRENT_OR_SELF); + brightnessFloat += stepFloat; + // Make sure we don't go beyond the limits. + brightnessFloat = Math.min(maxFloat, brightnessFloat); + brightnessFloat = Math.max(minFloat, brightnessFloat); + + Settings.System.putFloatForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightnessFloat, + UserHandle.USER_CURRENT_OR_SELF); + startActivityAsUser(new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG), + UserHandle.CURRENT_OR_SELF); + } + return key_consumed; + case KeyEvent.KEYCODE_VOLUME_UP: + case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_VOLUME_MUTE: + if (mUseTvRouting || mHandleVolumeKeysInWM) { + // On TVs or when the configuration is enabled, volume keys never + // go to the foreground app. + dispatchDirectAudioEvent(event); + return key_consumed; } - float minFloat = mPowerManager.getBrightnessConstraint( - PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM); - float maxFloat = mPowerManager.getBrightnessConstraint( - PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM); - float stepFloat = (maxFloat - minFloat) / BRIGHTNESS_STEPS * direction; - float brightnessFloat = Settings.System.getFloatForUser( - mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_FLOAT, - mContext.getDisplay().getBrightnessDefault(), - UserHandle.USER_CURRENT_OR_SELF); - brightnessFloat += stepFloat; - // Make sure we don't go beyond the limits. - brightnessFloat = Math.min(maxFloat, brightnessFloat); - brightnessFloat = Math.max(minFloat, brightnessFloat); - - Settings.System.putFloatForUser(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightnessFloat, - UserHandle.USER_CURRENT_OR_SELF); - startActivityAsUser(new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG), - UserHandle.CURRENT_OR_SELF); - } - return -1; - } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP - || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN - || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) { - if (mUseTvRouting || mHandleVolumeKeysInWM) { - // On TVs or when the configuration is enabled, volume keys never - // go to the foreground app. - dispatchDirectAudioEvent(event); - return -1; - } - // If the device is in VR mode and keys are "internal" (e.g. on the side of the - // device), then drop the volume keys and don't forward it to the application/dispatch - // the audio event. - if (mDefaultDisplayPolicy.isPersistentVrModeEnabled()) { - final InputDevice d = event.getDevice(); - if (d != null && !d.isExternal()) { - return -1; + // If the device is in VR mode and keys are "internal" (e.g. on the side of the + // device), then drop the volume keys and don't forward it to the + // application/dispatch the audio event. + if (mDefaultDisplayPolicy.isPersistentVrModeEnabled()) { + final InputDevice d = event.getDevice(); + if (d != null && !d.isExternal()) { + return key_consumed; + } } - } - } else if (keyCode == KeyEvent.KEYCODE_TAB && event.isMetaPressed()) { - // Pass through keyboard navigation keys. - return 0; - } else if (keyCode == KeyEvent.KEYCODE_ALL_APPS) { - if (!down) { - mHandler.removeMessages(MSG_HANDLE_ALL_APPS); - Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS); - msg.setAsynchronous(true); - msg.sendToTarget(); - } - return -1; - } else if (keyCode == KeyEvent.KEYCODE_NOTIFICATION) { - if (!down) { - toggleNotificationPanel(); - } - return -1; - } + break; + case KeyEvent.KEYCODE_TAB: + if (event.isMetaPressed()) { + // Pass through keyboard navigation keys. + return 0; + } + // Display task switcher for ALT-TAB. + if (down && repeatCount == 0) { + if (mRecentAppsHeldModifiers == 0 && !keyguardOn && isUserSetupComplete()) { + final int shiftlessModifiers = + event.getModifiers() & ~KeyEvent.META_SHIFT_MASK; + if (KeyEvent.metaStateHasModifiers( + shiftlessModifiers, KeyEvent.META_ALT_ON)) { + mRecentAppsHeldModifiers = shiftlessModifiers; + showRecentApps(true); + return key_consumed; + } + } + } + break; + case KeyEvent.KEYCODE_ALL_APPS: + if (!down) { + mHandler.removeMessages(MSG_HANDLE_ALL_APPS); + Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS); + msg.setAsynchronous(true); + msg.sendToTarget(); + } + return key_consumed; + case KeyEvent.KEYCODE_NOTIFICATION: + if (!down) { + toggleNotificationPanel(); + } + return key_consumed; - // Toggle Caps Lock on META-ALT. - boolean actionTriggered = false; - if (KeyEvent.isModifierKey(keyCode)) { - if (!mPendingCapsLockToggle) { - // Start tracking meta state for combo. - mInitialMetaState = mMetaState; - mPendingCapsLockToggle = true; - } else if (event.getAction() == KeyEvent.ACTION_UP) { - int altOnMask = mMetaState & KeyEvent.META_ALT_MASK; - int metaOnMask = mMetaState & KeyEvent.META_META_MASK; - - // Check for Caps Lock toggle - if ((metaOnMask != 0) && (altOnMask != 0)) { - // Check if nothing else is pressed - if (mInitialMetaState == (mMetaState ^ (altOnMask | metaOnMask))) { - // Handle Caps Lock Toggle + case KeyEvent.KEYCODE_SPACE: + // Handle keyboard layout switching. + if ((metaState & (KeyEvent.META_CTRL_MASK | KeyEvent.META_META_MASK)) == 0) { + return 0; + } + // Share the same behavior with KEYCODE_LANGUAGE_SWITCH. + case KeyEvent.KEYCODE_LANGUAGE_SWITCH: + if (down && repeatCount == 0) { + int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1; + mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction); + return key_consumed; + } + break; + case KeyEvent.KEYCODE_META_LEFT: + case KeyEvent.KEYCODE_META_RIGHT: + if (down) { + if (event.isAltPressed()) { + mPendingCapsLockToggle = true; + mPendingMetaAction = false; + } else { + mPendingCapsLockToggle = false; + mPendingMetaAction = true; + } + } else { + // Toggle Caps Lock on META-ALT. + if (mPendingCapsLockToggle) { mInputManagerInternal.toggleCapsLock(event.getDeviceId()); - actionTriggered = true; + mPendingCapsLockToggle = false; + } else if (mPendingMetaAction) { + launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD, + event.getDeviceId(), + event.getEventTime()); + mPendingMetaAction = false; } } + return key_consumed; + case KeyEvent.KEYCODE_ALT_LEFT: + case KeyEvent.KEYCODE_ALT_RIGHT: + if (down) { + if (event.isMetaPressed()) { + mPendingCapsLockToggle = true; + mPendingMetaAction = false; + } else { + mPendingCapsLockToggle = false; + } + } else { + // hide recent if triggered by ALT-TAB. + if (mRecentAppsHeldModifiers != 0 + && (metaState & mRecentAppsHeldModifiers) == 0) { + mRecentAppsHeldModifiers = 0; + hideRecentApps(true, false); + return key_consumed; + } - // Always stop tracking when key goes up. - mPendingCapsLockToggle = false; - } - } - // Store current meta state to be able to evaluate it later. - mMetaState = metaState; - - if (actionTriggered) { - return -1; - } - - if (KeyEvent.isMetaKey(keyCode)) { - if (down) { - mPendingMetaAction = true; - } else if (mPendingMetaAction) { - launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD, event.getDeviceId(), - event.getEventTime()); - } - return -1; + // Toggle Caps Lock on META-ALT. + if (mPendingCapsLockToggle) { + mInputManagerInternal.toggleCapsLock(event.getDeviceId()); + mPendingCapsLockToggle = false; + return key_consumed; + } + } + break; } // Shortcuts are invoked through Search+key, so intercept those here @@ -2803,7 +2798,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { + "SEARCH+" + KeyEvent.keyCodeToString(keyCode)); } } - return -1; + return key_consumed; } } @@ -2825,7 +2820,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { + "the activity to which it is registered was not found: " + "META+" + KeyEvent.keyCodeToString(keyCode), ex); } - return -1; + return key_consumed; } } } @@ -2844,39 +2839,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { + "the activity to which it is registered was not found: " + "keyCode=" + keyCode + ", category=" + category, ex); } - return -1; - } - } - - // Display task switcher for ALT-TAB. - if (down && repeatCount == 0 && keyCode == KeyEvent.KEYCODE_TAB) { - if (mRecentAppsHeldModifiers == 0 && !keyguardOn && isUserSetupComplete()) { - final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK; - if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON)) { - mRecentAppsHeldModifiers = shiftlessModifiers; - showRecentApps(true); - return -1; - } + return key_consumed; } - } else if (!down && mRecentAppsHeldModifiers != 0 - && (metaState & mRecentAppsHeldModifiers) == 0) { - mRecentAppsHeldModifiers = 0; - hideRecentApps(true, false); - } - - // Handle keyboard language switching. - final boolean isCtrlOrMetaSpace = keyCode == KeyEvent.KEYCODE_SPACE - && (metaState & (KeyEvent.META_CTRL_MASK | KeyEvent.META_META_MASK)) != 0; - if (down && repeatCount == 0 - && (keyCode == KeyEvent.KEYCODE_LANGUAGE_SWITCH || isCtrlOrMetaSpace)) { - int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1; - mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction); - return -1; } if (isValidGlobalKey(keyCode) && mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) { - return -1; + return key_consumed; } if (down) { @@ -2906,13 +2875,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { } catch (RemoteException e) { mShortcutKeyServices.delete(shortcutCode); } - return -1; + return key_consumed; } } // Reserve all the META modifier combos for system behavior if ((metaState & KeyEvent.META_META_ON) != 0) { - return -1; + return key_consumed; } // Let the application handle the key. @@ -3550,8 +3519,21 @@ public class PhoneWindowManager implements WindowManagerPolicy { return result; } + // Alternate TV power to power key for Android TV device. + final HdmiControlManager hdmiControlManager = getHdmiControlManager(); + if (keyCode == KeyEvent.KEYCODE_TV_POWER && mHasFeatureLeanback + && (hdmiControlManager == null || !hdmiControlManager.shouldHandleTvPowerKey())) { + event = KeyEvent.obtain( + event.getDownTime(), event.getEventTime(), + event.getAction(), KeyEvent.KEYCODE_POWER, + event.getRepeatCount(), event.getMetaState(), + event.getDeviceId(), event.getScanCode(), + event.getFlags(), event.getSource(), event.getDisplayId(), null); + return interceptKeyBeforeQueueing(event, policyFlags); + } + if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { - mKeyCombinationManager.interceptKey(event, interactive); + handleKeyGesture(event, interactive); } // Enable haptics if down and virtual key without multiple repetitions. If this is a hard @@ -3566,12 +3548,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { switch (keyCode) { case KeyEvent.KEYCODE_BACK: { if (down) { - interceptBackKeyDown(); + mBackKeyHandled = false; } else { - boolean handled = interceptBackKeyUp(event); - + if (!hasLongPressOnBackBehavior()) { + mBackKeyHandled |= backKeyPress(); + } // Don't pass back press to app if we've already handled it via long press - if (handled) { + if (mBackKeyHandled) { result &= ~ACTION_PASS_TO_USER; } } @@ -3683,33 +3666,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_TV_POWER: { result &= ~ACTION_PASS_TO_USER; isWakeKey = false; // wake-up will be handled separately - HdmiControlManager hdmiControlManager = getHdmiControlManager(); - if (hdmiControlManager != null && hdmiControlManager.shouldHandleTvPowerKey()) { - if (down) { - hdmiControlManager.toggleAndFollowTvPower(); - } - } else if (mHasFeatureLeanback) { - KeyEvent fallbackEvent = KeyEvent.obtain( - event.getDownTime(), event.getEventTime(), - event.getAction(), KeyEvent.KEYCODE_POWER, - event.getRepeatCount(), event.getMetaState(), - event.getDeviceId(), event.getScanCode(), - event.getFlags(), event.getSource(), event.getDisplayId(), null); - if (down) { - interceptPowerKeyDown(fallbackEvent, interactive); - } else { - interceptPowerKeyUp(fallbackEvent, interactive, canceled); - } + if (down && hdmiControlManager != null) { + hdmiControlManager.toggleAndFollowTvPower(); } - // Ignore this key for any device that is not connected to a TV via HDMI and - // not an Android TV device. break; } case KeyEvent.KEYCODE_POWER: { EventLogTags.writeInterceptPower( KeyEvent.actionToString(event.getAction()), - mPowerKeyHandled ? 1 : 0, mPowerKeyPressCounter); + mPowerKeyHandled ? 1 : 0, + mSingleKeyGestureDetector.getKeyPressCounter(KeyEvent.KEYCODE_POWER)); // Any activity on the power button stops the accessibility shortcut result &= ~ACTION_PASS_TO_USER; isWakeKey = false; // wake-up will be handled separately @@ -3890,6 +3857,43 @@ public class PhoneWindowManager implements WindowManagerPolicy { return result; } + private void handleKeyGesture(KeyEvent event, boolean interactive) { + if (mKeyCombinationManager.interceptKey(event, interactive)) { + // handled by combo keys manager. + mSingleKeyGestureDetector.reset(); + return; + } + + if (event.getKeyCode() == KEYCODE_POWER && event.getAction() == KeyEvent.ACTION_DOWN) { + mPowerKeyHandled = handleCameraGesture(event, interactive); + if (mPowerKeyHandled) { + // handled by camera gesture. + mSingleKeyGestureDetector.reset(); + return; + } + } + + mSingleKeyGestureDetector.interceptKey(event); + } + + // The camera gesture will be detected by GestureLauncherService. + private boolean handleCameraGesture(KeyEvent event, boolean interactive) { + // camera gesture. + GestureLauncherService gestureService = LocalServices.getService( + GestureLauncherService.class); + if (gestureService == null) { + return false; + } + + final MutableBoolean outLaunched = new MutableBoolean(false); + final boolean gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, + interactive, outLaunched); + if (outLaunched.value && mRequestedOrGoingToSleep) { + mCameraGestureTriggeredDuringGoingToSleep = true; + } + return gesturedServiceIntercepted; + } + /** * Handle statusbar expansion events. * @param event @@ -4889,15 +4893,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - private void schedulePossibleVeryLongPressReboot() { - mHandler.removeCallbacks(mPossibleVeryLongPressReboot); - mHandler.postDelayed(mPossibleVeryLongPressReboot, mVeryLongPressTimeout); - } - - private void cancelPossibleVeryLongPressReboot() { - mHandler.removeCallbacks(mPossibleVeryLongPressReboot); - } - // TODO (multidisplay): Support multiple displays in WindowManagerPolicy. private void updateScreenOffSleepToken(boolean acquire) { if (acquire) { diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java new file mode 100644 index 000000000000..3dafb0ce21ef --- /dev/null +++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java @@ -0,0 +1,356 @@ +/* + * 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.policy; + +import android.annotation.IntDef; +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Log; +import android.view.KeyEvent; +import android.view.ViewConfiguration; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; + +/** + * Detect single key gesture: press, long press, very long press and multi press. + * + * Call {@link #reset} if current {@link KeyEvent} has been handled by another policy + */ + +public final class SingleKeyGestureDetector { + private static final String TAG = "SingleKeyGesture"; + private static final boolean DEBUG = false; + + private static final int MSG_KEY_LONG_PRESS = 0; + private static final int MSG_KEY_VERY_LONG_PRESS = 1; + private static final int MSG_KEY_DELAYED_PRESS = 2; + + private final long mLongPressTimeout; + private final long mVeryLongPressTimeout; + + private volatile int mKeyPressCounter; + + private final ArrayList<SingleKeyRule> mRules = new ArrayList(); + private SingleKeyRule mActiveRule = null; + + // Key code of current key down event, reset when key up. + private int mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN; + private volatile boolean mHandledByLongPress = false; + private final Handler mHandler; + private static final long MULTI_PRESS_TIMEOUT = ViewConfiguration.getMultiPressTimeout(); + + + /** Supported gesture flags */ + public static final int KEY_LONGPRESS = 1 << 1; + public static final int KEY_VERYLONGPRESS = 1 << 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "KEY_" }, value = { + KEY_LONGPRESS, + KEY_VERYLONGPRESS, + }) + public @interface KeyGestureFlag {} + + /** + * Rule definition for single keys gesture. + * E.g : define power key. + * <pre class="prettyprint"> + * SingleKeyRule rule = + * new SingleKeyRule(KEYCODE_POWER, KEY_LONGPRESS|KEY_VERYLONGPRESS) { + * int getMaxMultiPressCount() { // maximum multi press count. } + * void onPress(long downTime) { // short press behavior. } + * void onLongPress() { // long press behavior. } + * void onVeryLongPress() { // very long press behavior. } + * void onMultiPress(long downTime, int count) { // multi press behavior. } + * }; + * </pre> + */ + abstract static class SingleKeyRule { + private final int mKeyCode; + private final int mSupportedGestures; + + SingleKeyRule(int keyCode, @KeyGestureFlag int supportedGestures) { + mKeyCode = keyCode; + mSupportedGestures = supportedGestures; + } + + /** + * True if the rule could intercept the key. + */ + private boolean shouldInterceptKey(int keyCode) { + return keyCode == mKeyCode; + } + + /** + * True if the rule support long press. + */ + private boolean supportLongPress() { + return (mSupportedGestures & KEY_LONGPRESS) != 0; + } + + /** + * True if the rule support very long press. + */ + private boolean supportVeryLongPress() { + return (mSupportedGestures & KEY_VERYLONGPRESS) != 0; + } + + /** + * Maximum count of multi presses. + * Return 1 will trigger onPress immediately when {@link KeyEvent.ACTION_UP}. + * Otherwise trigger onMultiPress immediately when reach max count when + * {@link KeyEvent.ACTION_DOWN}. + */ + int getMaxMultiPressCount() { + return 1; + } + + /** + * Called when short press has been detected. + */ + abstract void onPress(long downTime); + /** + * Callback when multi press (>= 2) has been detected. + */ + void onMultiPress(long downTime, int count) {} + /** + * Callback when long press has been detected. + */ + void onLongPress(long downTime) {} + /** + * Callback when very long press has been detected. + */ + void onVeryLongPress(long downTime) {} + + @Override + public String toString() { + return "KeyCode = " + KeyEvent.keyCodeToString(mKeyCode) + + ", long press : " + supportLongPress() + + ", very Long press : " + supportVeryLongPress() + + ", max multi press count : " + getMaxMultiPressCount(); + } + } + + public SingleKeyGestureDetector(Context context) { + mLongPressTimeout = ViewConfiguration.get(context).getDeviceGlobalActionKeyTimeout(); + mVeryLongPressTimeout = context.getResources().getInteger( + com.android.internal.R.integer.config_veryLongPressTimeout); + mHandler = new KeyHandler(); + } + + void addRule(SingleKeyRule rule) { + mRules.add(rule); + } + + void interceptKey(KeyEvent event) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + interceptKeyDown(event); + } else { + interceptKeyUp(event); + } + } + + private void interceptKeyDown(KeyEvent event) { + final int keyCode = event.getKeyCode(); + // same key down. + if (mDownKeyCode == keyCode) { + if (mActiveRule != null && (event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0 + && !mHandledByLongPress) { + if (DEBUG) { + Log.i(TAG, "Long press Key " + KeyEvent.keyCodeToString(keyCode)); + } + mHandledByLongPress = true; + mHandler.removeMessages(MSG_KEY_LONG_PRESS); + mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS); + mActiveRule.onLongPress(event.getEventTime()); + } + return; + } + + // When a different key is pressed, stop processing gestures for the currently active key. + if (mDownKeyCode != KeyEvent.KEYCODE_UNKNOWN + || (mActiveRule != null && !mActiveRule.shouldInterceptKey(keyCode))) { + if (DEBUG) { + Log.i(TAG, "Press another key " + KeyEvent.keyCodeToString(keyCode)); + } + + reset(); + } + mDownKeyCode = keyCode; + + // Picks a new rule, return if no rule picked. + if (mActiveRule == null) { + final int count = mRules.size(); + for (int index = 0; index < count; index++) { + final SingleKeyRule rule = mRules.get(index); + if (rule.shouldInterceptKey(keyCode)) { + mActiveRule = rule; + break; + } + } + } + if (mActiveRule == null) { + return; + } + + final long eventTime = event.getEventTime(); + if (mKeyPressCounter == 0) { + if (mActiveRule.supportLongPress()) { + final Message msg = mHandler.obtainMessage(MSG_KEY_LONG_PRESS, keyCode, 0, + eventTime); + msg.setAsynchronous(true); + mHandler.sendMessageDelayed(msg, mLongPressTimeout); + } + + if (mActiveRule.supportVeryLongPress()) { + final Message msg = mHandler.obtainMessage(MSG_KEY_VERY_LONG_PRESS, keyCode, 0, + eventTime); + msg.setAsynchronous(true); + mHandler.sendMessageDelayed(msg, mVeryLongPressTimeout); + } + } else { + mHandler.removeMessages(MSG_KEY_LONG_PRESS); + mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS); + mHandler.removeMessages(MSG_KEY_DELAYED_PRESS); + + // Trigger multi press immediately when reach max count.( > 1) + if (mKeyPressCounter == mActiveRule.getMaxMultiPressCount() - 1) { + if (DEBUG) { + Log.i(TAG, "Trigger multi press " + mActiveRule.toString() + " for it" + + " reach the max count " + mKeyPressCounter); + } + mActiveRule.onMultiPress(eventTime, mKeyPressCounter + 1); + mKeyPressCounter = 0; + } + } + } + + private boolean interceptKeyUp(KeyEvent event) { + mHandler.removeMessages(MSG_KEY_LONG_PRESS); + mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS); + mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN; + if (mActiveRule == null) { + return false; + } + + if (mHandledByLongPress) { + mHandledByLongPress = false; + return true; + } + + final long downTime = event.getDownTime(); + if (event.getKeyCode() == mActiveRule.mKeyCode) { + // Directly trigger short press when max count is 1. + if (mActiveRule.getMaxMultiPressCount() == 1) { + mActiveRule.onPress(downTime); + return true; + } + + // This could be a multi-press. Wait a little bit longer to confirm. + mKeyPressCounter++; + Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, mActiveRule.mKeyCode, + mKeyPressCounter, downTime); + msg.setAsynchronous(true); + mHandler.sendMessageDelayed(msg, MULTI_PRESS_TIMEOUT); + return true; + } + reset(); + return false; + } + + int getKeyPressCounter(int keyCode) { + if (mActiveRule != null && mActiveRule.mKeyCode == keyCode) { + return mKeyPressCounter; + } else { + return 0; + } + } + + void reset() { + if (mActiveRule != null) { + if (mDownKeyCode != KeyEvent.KEYCODE_UNKNOWN) { + mHandler.removeMessages(MSG_KEY_LONG_PRESS); + mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS); + } + + if (mKeyPressCounter > 0) { + mHandler.removeMessages(MSG_KEY_DELAYED_PRESS); + mKeyPressCounter = 0; + } + mActiveRule = null; + } + + mHandledByLongPress = false; + mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN; + } + + boolean isKeyIntercepted(int keyCode) { + if (mActiveRule != null && mActiveRule.shouldInterceptKey(keyCode)) { + return mHandledByLongPress; + } + return false; + } + + private class KeyHandler extends Handler { + KeyHandler() { + super(Looper.getMainLooper()); + } + + @Override + public void handleMessage(Message msg) { + if (mActiveRule == null) { + return; + } + final int keyCode = msg.arg1; + final long eventTime = (long) msg.obj; + switch(msg.what) { + case MSG_KEY_LONG_PRESS: + if (DEBUG) { + Log.i(TAG, "Detect long press " + KeyEvent.keyCodeToString(keyCode)); + } + mHandledByLongPress = true; + mActiveRule.onLongPress(eventTime); + break; + case MSG_KEY_VERY_LONG_PRESS: + if (DEBUG) { + Log.i(TAG, "Detect very long press " + + KeyEvent.keyCodeToString(keyCode)); + } + mHandledByLongPress = true; + mActiveRule.onVeryLongPress(eventTime); + break; + case MSG_KEY_DELAYED_PRESS: + if (DEBUG) { + Log.i(TAG, "Detect press " + KeyEvent.keyCodeToString(keyCode) + + ", count " + mKeyPressCounter); + } + if (mKeyPressCounter == 1) { + mActiveRule.onPress(eventTime); + } else { + mActiveRule.onMultiPress(eventTime, mKeyPressCounter); + } + mKeyPressCounter = 0; + break; + } + } + } +} diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 8c46445fac9b..bc117094dd68 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -1645,7 +1645,7 @@ public final class PowerManagerService extends SystemService } // Called from native code. - private void userActivityFromNative(long eventTime, int event, int flags) { + private void userActivityFromNative(long eventTime, int event, int displayId, int flags) { userActivityInternal(eventTime, event, flags, Process.SYSTEM_UID); } diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java index c4f29ea69218..ef0079e0c01f 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java @@ -16,6 +16,8 @@ package com.android.server.powerstats; +import static java.lang.System.currentTimeMillis; + import android.content.Context; import android.hardware.power.stats.Channel; import android.hardware.power.stats.EnergyConsumer; @@ -26,11 +28,14 @@ import android.hardware.power.stats.StateResidencyResult; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.SystemClock; import android.util.AtomicFile; import android.util.Slog; import android.util.proto.ProtoInputStream; import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.VisibleForTesting; + import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper; import com.android.server.powerstats.ProtoStreamUtils.ChannelUtils; import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerResultUtils; @@ -61,6 +66,8 @@ public final class PowerStatsLogger extends Handler { protected static final int MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY = 1; protected static final int MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY = 2; + // TODO(b/181240441): Add a listener to update the Wall clock baseline when changed + private final long mStartWallTime; private final PowerStatsDataStorage mPowerStatsMeterStorage; private final PowerStatsDataStorage mPowerStatsModelStorage; private final PowerStatsDataStorage mPowerStatsResidencyStorage; @@ -79,6 +86,8 @@ public final class PowerStatsLogger extends Handler { // Log power meter data. EnergyMeasurement[] energyMeasurements = mPowerStatsHALWrapper.readEnergyMeter(new int[0]); + EnergyMeasurementUtils.adjustTimeSinceBootToEpoch(energyMeasurements, + mStartWallTime); mPowerStatsMeterStorage.write( EnergyMeasurementUtils.getProtoBytes(energyMeasurements)); if (DEBUG) EnergyMeasurementUtils.print(energyMeasurements); @@ -86,6 +95,8 @@ public final class PowerStatsLogger extends Handler { // Log power model data without attribution data. EnergyConsumerResult[] ecrNoAttribution = mPowerStatsHALWrapper.getEnergyConsumed(new int[0]); + EnergyConsumerResultUtils.adjustTimeSinceBootToEpoch(ecrNoAttribution, + mStartWallTime); mPowerStatsModelStorage.write( EnergyConsumerResultUtils.getProtoBytes(ecrNoAttribution, false)); if (DEBUG) EnergyConsumerResultUtils.print(ecrNoAttribution); @@ -97,6 +108,8 @@ public final class PowerStatsLogger extends Handler { // Log power model data with attribution data. EnergyConsumerResult[] ecrAttribution = mPowerStatsHALWrapper.getEnergyConsumed(new int[0]); + EnergyConsumerResultUtils.adjustTimeSinceBootToEpoch(ecrAttribution, + mStartWallTime); mPowerStatsModelStorage.write( EnergyConsumerResultUtils.getProtoBytes(ecrAttribution, true)); if (DEBUG) EnergyConsumerResultUtils.print(ecrAttribution); @@ -108,6 +121,8 @@ public final class PowerStatsLogger extends Handler { // Log state residency data. StateResidencyResult[] stateResidencyResults = mPowerStatsHALWrapper.getStateResidency(new int[0]); + StateResidencyResultUtils.adjustTimeSinceBootToEpoch(stateResidencyResults, + mStartWallTime); mPowerStatsResidencyStorage.write( StateResidencyResultUtils.getProtoBytes(stateResidencyResults)); if (DEBUG) StateResidencyResultUtils.print(stateResidencyResults); @@ -293,12 +308,19 @@ public final class PowerStatsLogger extends Handler { return mDeleteResidencyDataOnBoot; } + @VisibleForTesting + public long getStartWallTime() { + return mStartWallTime; + } + public PowerStatsLogger(Context context, File dataStoragePath, String meterFilename, String meterCacheFilename, String modelFilename, String modelCacheFilename, String residencyFilename, String residencyCacheFilename, IPowerStatsHALWrapper powerStatsHALWrapper) { super(Looper.getMainLooper()); + mStartWallTime = currentTimeMillis() - SystemClock.elapsedRealtime(); + if (DEBUG) Slog.d(TAG, "mStartWallTime: " + mStartWallTime); mPowerStatsHALWrapper = powerStatsHALWrapper; mDataStoragePath = dataStoragePath; diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java index 11b22a574476..746a09882f93 100644 --- a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java +++ b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java @@ -109,6 +109,18 @@ public class ProtoStreamUtils { } static class StateResidencyResultUtils { + public static void adjustTimeSinceBootToEpoch(StateResidencyResult[] stateResidencyResult, + long startWallTime) { + for (int i = 0; i < stateResidencyResult.length; i++) { + final int stateLength = stateResidencyResult[i].stateResidencyData.length; + for (int j = 0; j < stateLength; j++) { + final StateResidency stateResidencyData = + stateResidencyResult[i].stateResidencyData[j]; + stateResidencyData.lastEntryTimestampMs += startWallTime; + } + } + } + public static byte[] getProtoBytes(StateResidencyResult[] stateResidencyResult) { ProtoOutputStream pos = new ProtoOutputStream(); packProtoMessage(stateResidencyResult, pos); @@ -306,6 +318,13 @@ public class ProtoStreamUtils { } static class EnergyMeasurementUtils { + public static void adjustTimeSinceBootToEpoch(EnergyMeasurement[] energyMeasurement, + long startWallTime) { + for (int i = 0; i < energyMeasurement.length; i++) { + energyMeasurement[i].timestampMs += startWallTime; + } + } + public static byte[] getProtoBytes(EnergyMeasurement[] energyMeasurement) { ProtoOutputStream pos = new ProtoOutputStream(); packProtoMessage(energyMeasurement, pos); @@ -518,6 +537,13 @@ public class ProtoStreamUtils { } static class EnergyConsumerResultUtils { + public static void adjustTimeSinceBootToEpoch(EnergyConsumerResult[] energyConsumerResult, + long startWallTime) { + for (int i = 0; i < energyConsumerResult.length; i++) { + energyConsumerResult[i].timestampMs += startWallTime; + } + } + public static byte[] getProtoBytes(EnergyConsumerResult[] energyConsumerResult, boolean includeAttribution) { ProtoOutputStream pos = new ProtoOutputStream(); 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/timedetector/DeviceConfig.java b/services/core/java/com/android/server/timedetector/DeviceConfig.java deleted file mode 100644 index 7b9ad0f531aa..000000000000 --- a/services/core/java/com/android/server/timedetector/DeviceConfig.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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.timedetector; - -import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.StringDef; - -import java.time.Duration; -import java.util.concurrent.Executor; - -/** - * A helper class for reading / monitoring the {@link - * android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME} namespace for server-configured flags. - */ -public final class DeviceConfig { - - /** - * An annotation used to indicate when a {@link - * android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME} key is required. - * - * <p>Note that the com.android.geotz module deployment of the Offline LocationTimeZoneProvider - * also shares the {@link android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME}, and uses the - * prefix "geotz_" on all of its key strings. - */ - @StringDef(prefix = "KEY_", value = { - KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED, - KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT, - KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS, - KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS, - KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS, - }) - @interface DeviceConfigKey {} - - /** - * The key to force location time zone detection on for a device. Only intended for use during - * release testing with droidfooders. The user can still disable the feature by turning off the - * master location switch, or disabling automatic time zone detection. - */ - @DeviceConfigKey - public static final String KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED = - "force_location_time_zone_detection_enabled"; - - /** - * The key for the default value used to determine whether location time zone detection is - * enabled when the user hasn't explicitly set it yet. - */ - @DeviceConfigKey - public static final String KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT = - "location_time_zone_detection_enabled_default"; - - /** - * The key for the minimum delay after location time zone detection has been enabled before the - * location time zone manager can report it is uncertain about the time zone. - */ - @DeviceConfigKey - public static final String KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS = - "location_time_zone_detection_uncertainty_delay_millis"; - - /** - * The key for the timeout passed to a location time zone provider that tells it how long it has - * to provide an explicit first suggestion without being declared uncertain. - */ - @DeviceConfigKey - public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS = - "ltpz_init_timeout_millis"; - - /** - * The key for the extra time added to {@link - * #KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS} by the location time zone - * manager before the location time zone provider will actually be declared uncertain. - */ - @DeviceConfigKey - public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS = - "ltpz_init_timeout_fuzz_millis"; - - /** Creates an instance. */ - public DeviceConfig() {} - - /** Adds a listener for the system_time namespace. */ - public void addListener( - @NonNull Executor handlerExecutor, @NonNull Runnable listener) { - android.provider.DeviceConfig.addOnPropertiesChangedListener( - NAMESPACE_SYSTEM_TIME, - handlerExecutor, - properties -> listener.run()); - } - - /** - * Returns a boolean value from {@link android.provider.DeviceConfig} from the system_time - * namespace, or {@code defaultValue} if there is no explicit value set. - */ - public boolean getBoolean(@DeviceConfigKey String key, boolean defaultValue) { - return android.provider.DeviceConfig.getBoolean(NAMESPACE_SYSTEM_TIME, key, defaultValue); - } - - /** - * Returns a positive duration from {@link android.provider.DeviceConfig} from the system_time - * namespace, or {@code defaultValue} if there is no explicit value set. - */ - @Nullable - public Duration getDurationFromMillis( - @DeviceConfigKey String key, @Nullable Duration defaultValue) { - long deviceConfigValue = - android.provider.DeviceConfig.getLong(NAMESPACE_SYSTEM_TIME, key, -1); - if (deviceConfigValue < 0) { - return defaultValue; - } - return Duration.ofMillis(deviceConfigValue); - } -} diff --git a/services/core/java/com/android/server/timedetector/ServerFlags.java b/services/core/java/com/android/server/timedetector/ServerFlags.java new file mode 100644 index 000000000000..8819b371b225 --- /dev/null +++ b/services/core/java/com/android/server/timedetector/ServerFlags.java @@ -0,0 +1,238 @@ +/* + * 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.timedetector; + +import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.StringDef; +import android.content.Context; +import android.provider.DeviceConfig; +import android.util.ArrayMap; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.timezonedetector.ConfigurationChangeListener; +import com.android.server.timezonedetector.ServiceConfigAccessor; + +import java.time.Duration; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; + +/** + * A helper class for reading / monitoring the {@link DeviceConfig#NAMESPACE_SYSTEM_TIME} namespace + * for server-configured flags. + */ +public final class ServerFlags { + + private static final Optional<Boolean> OPTIONAL_TRUE = Optional.of(true); + private static final Optional<Boolean> OPTIONAL_FALSE = Optional.of(false); + + /** + * An annotation used to indicate when a {@link DeviceConfig#NAMESPACE_SYSTEM_TIME} key is + * required. + * + * <p>Note that the com.android.geotz module deployment of the Offline LocationTimeZoneProvider + * also shares the {@link DeviceConfig#NAMESPACE_SYSTEM_TIME}, and uses the + * prefix "geotz_" on all of its key strings. + */ + @StringDef(prefix = "KEY_", value = { + KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED, + KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE, + KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE, + KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS, + KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS, + KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS, + KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE, + KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT, + }) + @interface DeviceConfigKey {} + + /** + * Controls whether the location time zone manager service will started. Only observed if + * the device build is configured to support location-based time zone detection. See + * {@link ServiceConfigAccessor#isGeoTimeZoneDetectionFeatureSupportedInConfig()} and {@link + * ServiceConfigAccessor#isGeoTimeZoneDetectionFeatureSupported()}. + */ + @DeviceConfigKey + public static final String KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED = + "location_time_zone_detection_feature_supported"; + + /** + * The key for the server flag that can override the device config for whether the primary + * location time zone provider is enabled or disabled. + */ + @DeviceConfigKey + public static final String KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE = + "primary_location_time_zone_provider_enabled_override"; + + /** + * The key for the server flag that can override the device config for whether the secondary + * location time zone provider is enabled or disabled. + */ + @DeviceConfigKey + public static final String KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE = + "secondary_location_time_zone_provider_enabled_override"; + + /** + * The key for the minimum delay after location time zone detection has been enabled before the + * location time zone manager can report it is uncertain about the time zone. + */ + @DeviceConfigKey + public static final String KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS = + "location_time_zone_detection_uncertainty_delay_millis"; + + /** + * The key for the timeout passed to a location time zone provider that tells it how long it has + * to provide an explicit first suggestion without being declared uncertain. + */ + @DeviceConfigKey + public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS = + "ltpz_init_timeout_millis"; + + /** + * The key for the extra time added to {@link + * #KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS} by the location time zone + * manager before the location time zone provider will actually be declared uncertain. + */ + @DeviceConfigKey + public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS = + "ltpz_init_timeout_fuzz_millis"; + + /** + * The key for the server flag that can override location time zone detection being enabled for + * a user. Only intended for use during release testing with droidfooders. The user can still + * disable the feature by turning off the master location switch, or by disabling automatic time + * zone detection. + */ + @DeviceConfigKey + public static final String KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE = + "location_time_zone_detection_setting_enabled_override"; + + /** + * The key for the default value used to determine whether location time zone detection is + * enabled when the user hasn't explicitly set it yet. + */ + @DeviceConfigKey + public static final String KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT = + "location_time_zone_detection_setting_enabled_default"; + + @GuardedBy("mListeners") + private final ArrayMap<ConfigurationChangeListener, Set<String>> mListeners = new ArrayMap<>(); + + private static final Object SLOCK = new Object(); + + @GuardedBy("SLOCK") + @Nullable + private static ServerFlags sInstance; + + private ServerFlags(Context context) { + DeviceConfig.addOnPropertiesChangedListener( + NAMESPACE_SYSTEM_TIME, + context.getMainExecutor(), + this::handlePropertiesChanged); + } + + /** Returns the singleton instance. */ + public static ServerFlags getInstance(Context context) { + synchronized (SLOCK) { + if (sInstance == null) { + sInstance = new ServerFlags(context); + } + return sInstance; + } + } + + private void handlePropertiesChanged(@NonNull DeviceConfig.Properties properties) { + synchronized (mListeners) { + for (Map.Entry<ConfigurationChangeListener, Set<String>> listenerEntry + : mListeners.entrySet()) { + if (intersects(listenerEntry.getValue(), properties.getKeyset())) { + listenerEntry.getKey().onChange(); + } + } + } + } + + private static boolean intersects(@NonNull Set<String> one, @NonNull Set<String> two) { + for (String toFind : one) { + if (two.contains(toFind)) { + return true; + } + } + return false; + } + + /** + * Adds a listener for the system_time namespace that will trigger if any of the specified keys + * change. Listener callbacks are delivered on the main looper thread. + * + * <p>Note: Only for use by long-lived objects like other singletons. There is deliberately no + * associated remove method. + */ + public void addListener(@NonNull ConfigurationChangeListener listener, + @NonNull Set<String> keys) { + Objects.requireNonNull(listener); + Objects.requireNonNull(keys); + + synchronized (mListeners) { + mListeners.put(listener, keys); + } + } + + /** + * Returns an optional boolean value from {@link DeviceConfig} from the system_time + * namespace, returns {@link Optional#empty()} if there is no explicit value set. + */ + @NonNull + public Optional<Boolean> getOptionalBoolean(@DeviceConfigKey String key) { + String value = DeviceConfig.getProperty(NAMESPACE_SYSTEM_TIME, key); + return parseOptionalBoolean(value); + } + + @NonNull + private static Optional<Boolean> parseOptionalBoolean(@Nullable String value) { + if (value == null) { + return Optional.empty(); + } else { + return Boolean.parseBoolean(value) ? OPTIONAL_TRUE : OPTIONAL_FALSE; + } + } + + /** + * Returns a boolean value from {@link DeviceConfig} from the system_time + * namespace, or {@code defaultValue} if there is no explicit value set. + */ + public boolean getBoolean(@DeviceConfigKey String key, boolean defaultValue) { + return DeviceConfig.getBoolean(NAMESPACE_SYSTEM_TIME, key, defaultValue); + } + + /** + * Returns a positive duration from {@link DeviceConfig} from the system_time + * namespace, or {@code defaultValue} if there is no explicit value set. + */ + @Nullable + public Duration getDurationFromMillis( + @DeviceConfigKey String key, @Nullable Duration defaultValue) { + long deviceConfigValue = DeviceConfig.getLong(NAMESPACE_SYSTEM_TIME, key, -1); + if (deviceConfigValue < 0) { + return defaultValue; + } + return Duration.ofMillis(deviceConfigValue); + } +} diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java b/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java index 4c7b1f38dd5a..aa8ad37815bf 100644 --- a/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java +++ b/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java @@ -17,10 +17,11 @@ package com.android.server.timezonedetector; /** - * A listener used to receive notification that time zone configuration has changed. + * A listener used to receive notification that configuration has / may have changed (depending on + * the usecase). */ @FunctionalInterface public interface ConfigurationChangeListener { - /** Called when the current user or a configuration value has changed. */ + /** Called when the configuration may have changed. */ void onChange(); } diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java index 1f73977444f8..3ae9d641e81c 100644 --- a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java +++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java @@ -33,26 +33,27 @@ import com.android.internal.util.Preconditions; import java.util.Objects; /** - * Holds all configuration values that affect time zone behavior and some associated logic, e.g. - * {@link #getAutoDetectionEnabledBehavior()}, {@link #getGeoDetectionEnabledBehavior()} and {@link - * #createCapabilitiesAndConfig()}. + * Holds configuration values that affect user-facing time zone behavior and some associated logic. + * Some configuration is global, some is user scoped, but this class deliberately doesn't make a + * distinction for simplicity. */ public final class ConfigurationInternal { - private final @UserIdInt int mUserId; - private final boolean mUserConfigAllowed; private final boolean mAutoDetectionSupported; private final boolean mGeoDetectionSupported; private final boolean mAutoDetectionEnabled; + private final @UserIdInt int mUserId; + private final boolean mUserConfigAllowed; private final boolean mLocationEnabled; private final boolean mGeoDetectionEnabled; private ConfigurationInternal(Builder builder) { - mUserId = builder.mUserId; - mUserConfigAllowed = builder.mUserConfigAllowed; mAutoDetectionSupported = builder.mAutoDetectionSupported; mGeoDetectionSupported = builder.mGeoDetectionSupported; mAutoDetectionEnabled = builder.mAutoDetectionEnabled; + + mUserId = builder.mUserId; + mUserConfigAllowed = builder.mUserConfigAllowed; mLocationEnabled = builder.mLocationEnabled; mGeoDetectionEnabled = builder.mGeoDetectionEnabled; // if mGeoDetectionSupported then mAutoDetectionSupported, i.e. mGeoDetectionSupported @@ -60,22 +61,6 @@ public final class ConfigurationInternal { Preconditions.checkState(mAutoDetectionSupported || !mGeoDetectionSupported); } - /** Returns the ID of the user this configuration is associated with. */ - public @UserIdInt int getUserId() { - return mUserId; - } - - /** Returns the handle of the user this configuration is associated with. */ - @NonNull - public UserHandle getUserHandle() { - return UserHandle.of(mUserId); - } - - /** Returns true if the user allowed to modify time zone configuration. */ - public boolean isUserConfigAllowed() { - return mUserConfigAllowed; - } - /** Returns true if the device supports any form of auto time zone detection. */ public boolean isAutoDetectionSupported() { return mAutoDetectionSupported; @@ -98,6 +83,22 @@ public final class ConfigurationInternal { return mAutoDetectionSupported && mAutoDetectionEnabled; } + /** Returns the ID of the user this configuration is associated with. */ + public @UserIdInt int getUserId() { + return mUserId; + } + + /** Returns the handle of the user this configuration is associated with. */ + @NonNull + public UserHandle getUserHandle() { + return UserHandle.of(mUserId); + } + + /** Returns true if the user allowed to modify time zone configuration. */ + public boolean isUserConfigAllowed() { + return mUserConfigAllowed; + } + /** Returns true if user's location can be used generally. */ public boolean isLocationEnabled() { return mLocationEnabled; @@ -283,7 +284,7 @@ public final class ConfigurationInternal { /** * Sets whether any form of automatic time zone detection is supported on this device. */ - public Builder setAutoDetectionSupported(boolean supported) { + public Builder setAutoDetectionFeatureSupported(boolean supported) { mAutoDetectionSupported = supported; return this; } @@ -291,7 +292,7 @@ public final class ConfigurationInternal { /** * Sets whether geolocation time zone detection is supported on this device. */ - public Builder setGeoDetectionSupported(boolean supported) { + public Builder setGeoDetectionFeatureSupported(boolean supported) { mGeoDetectionSupported = supported; return this; } diff --git a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java index f52b9b1b1c58..e3caae9482d9 100644 --- a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java +++ b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java @@ -31,9 +31,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.database.ContentObserver; import android.location.LocationManager; -import android.net.ConnectivityManager; import android.os.Handler; -import android.os.HandlerExecutor; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; @@ -42,10 +40,9 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.server.LocalServices; -import com.android.server.timedetector.DeviceConfig; import java.util.Objects; -import java.util.concurrent.Executor; +import java.util.Optional; /** * The real implementation of {@link TimeZoneDetectorStrategyImpl.Environment}. @@ -59,8 +56,7 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir @NonNull private final Handler mHandler; @NonNull private final ContentResolver mCr; @NonNull private final UserManager mUserManager; - @NonNull private final DeviceConfig mDeviceConfig; - @NonNull private final boolean mGeoDetectionSupported; + @NonNull private final ServiceConfigAccessor mServiceConfigAccessor; @NonNull private final LocationManager mLocationManager; // @NonNull after setConfigChangeListener() is called. @@ -68,17 +64,16 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir private ConfigurationChangeListener mConfigChangeListener; EnvironmentImpl(@NonNull Context context, @NonNull Handler handler, - @NonNull DeviceConfig deviceConfig, boolean geoDetectionSupported) { + @NonNull ServiceConfigAccessor serviceConfigAccessor) { mContext = Objects.requireNonNull(context); mHandler = Objects.requireNonNull(handler); - Executor handlerExecutor = new HandlerExecutor(mHandler); mCr = context.getContentResolver(); mUserManager = context.getSystemService(UserManager.class); mLocationManager = context.getSystemService(LocationManager.class); - mDeviceConfig = deviceConfig; - mGeoDetectionSupported = geoDetectionSupported; + mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor); - // Wire up the change listeners. All invocations are performed on the mHandler thread. + // Wire up the config change listeners. All invocations are performed on the mHandler + // thread. // Listen for the user changing / the user's location mode changing. IntentFilter filter = new IntentFilter(); @@ -112,13 +107,6 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir handleConfigChangeOnHandlerThread(); } }, UserHandle.USER_ALL); - - // Add async callbacks for changes to server-side flags: some of the flags affect device / - // user config. All changes can be treated like a config change. If flags that affect config - // haven't changed then call will be a no-op. - mDeviceConfig.addListener( - handlerExecutor, - this::handleConfigChangeOnHandlerThread); } private void handleConfigChangeOnHandlerThread() { @@ -140,10 +128,12 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir @Override public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) { return new ConfigurationInternal.Builder(userId) - .setUserConfigAllowed(isUserConfigAllowed(userId)) - .setAutoDetectionSupported(isAutoDetectionSupported()) - .setGeoDetectionSupported(isGeoDetectionSupported()) + .setAutoDetectionFeatureSupported( + mServiceConfigAccessor.isAutoDetectionFeatureSupported()) + .setGeoDetectionFeatureSupported( + mServiceConfigAccessor.isGeoTimeZoneDetectionFeatureSupported()) .setAutoDetectionEnabled(isAutoDetectionEnabled()) + .setUserConfigAllowed(isUserConfigAllowed(userId)) .setLocationEnabled(isLocationEnabled(userId)) .setGeoDetectionEnabled(isGeoDetectionEnabled(userId)) .build(); @@ -186,18 +176,19 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir // time zone detection: if we wrote it down then we'd set the value explicitly, which would // prevent detecting "default" later. That might influence what happens on later releases // that support new types of auto detection on the same hardware. - if (isAutoDetectionSupported()) { + if (mServiceConfigAccessor.isAutoDetectionFeatureSupported()) { final boolean autoDetectionEnabled = configuration.isAutoDetectionEnabled(); setAutoDetectionEnabledIfRequired(autoDetectionEnabled); - // Avoid writing the geo detection enabled setting for devices that do not support geo - // time zone detection: if we wrote it down then we'd set the value explicitly, which - // would prevent detecting "default" later. That might influence what happens on later - // releases that support geo detection on the same hardware. - // Also avoid writing the geo detection enabled setting for devices that are currently - // force-enabled: otherwise we might overwrite a droidfood user's real setting - // permanently. - if (isGeoDetectionSupported() && !isGeoDetectionForceEnabled()) { + // Avoid writing the geo detection enabled setting for devices with settings that + // are currently overridden by server flags: otherwise we might overwrite a droidfood + // user's real setting permanently. + // Also avoid writing the geo detection enabled setting for devices that do not support + // geo time zone detection: if we wrote it down then we'd set the value explicitly, + // which would prevent detecting "default" later. That might influence what happens on + // later releases that start to support geo detection on the same hardware. + if (!mServiceConfigAccessor.getGeoDetectionSettingEnabledOverride().isPresent() + && mServiceConfigAccessor.isGeoTimeZoneDetectionFeatureSupported()) { final boolean geoTzDetectionEnabled = configuration.isGeoDetectionEnabled(); setGeoDetectionEnabledIfRequired(userId, geoTzDetectionEnabled); } @@ -209,14 +200,6 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME, userHandle); } - private boolean isAutoDetectionSupported() { - return deviceHasTelephonyNetwork() || isGeoDetectionSupported(); - } - - private boolean isGeoDetectionSupported() { - return mGeoDetectionSupported; - } - private boolean isAutoDetectionEnabled() { return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE, 1 /* default */) > 0; } @@ -237,24 +220,20 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir private boolean isGeoDetectionEnabled(@UserIdInt int userId) { // We may never use this, but it gives us a way to force location-based time zone detection - // on for testers (where their other settings allow). - boolean forceEnabled = isGeoDetectionForceEnabled(); - if (forceEnabled) { - return true; + // on/off for testers (but only where their other settings would allow them to turn it on + // for themselves). + Optional<Boolean> override = mServiceConfigAccessor.getGeoDetectionSettingEnabledOverride(); + if (override.isPresent()) { + return override.get(); } - final boolean geoDetectionEnabledByDefault = mDeviceConfig.getBoolean( - DeviceConfig.KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT, false); + final boolean geoDetectionEnabledByDefault = + mServiceConfigAccessor.isGeoDetectionEnabledForUsersByDefault(); return Settings.Secure.getIntForUser(mCr, Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED, (geoDetectionEnabledByDefault ? 1 : 0) /* defaultValue */, userId) != 0; } - private boolean isGeoDetectionForceEnabled() { - return mDeviceConfig.getBoolean( - DeviceConfig.KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED, false); - } - private void setGeoDetectionEnabledIfRequired(@UserIdInt int userId, boolean enabled) { // See comment in setAutoDetectionEnabledIfRequired. http://b/171953500 if (isGeoDetectionEnabled(userId) != enabled) { @@ -262,10 +241,4 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir enabled ? 1 : 0, userId); } } - - private boolean deviceHasTelephonyNetwork() { - // TODO b/150583524 Avoid the use of a deprecated API. - return mContext.getSystemService(ConnectivityManager.class) - .isNetworkSupported(ConnectivityManager.TYPE_MOBILE); - } } diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java new file mode 100644 index 000000000000..86c32f8d7b45 --- /dev/null +++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java @@ -0,0 +1,249 @@ +/* + * 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.timezonedetector; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.content.res.Resources; +import android.net.ConnectivityManager; +import android.os.SystemProperties; +import android.util.ArraySet; + +import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; +import com.android.server.timedetector.ServerFlags; + +import java.time.Duration; +import java.util.Collections; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; + +/** + * A singleton that provides access to service configuration for time zone detection. This hides how + * configuration is split between static, compile-time config and dynamic, server-pushed flags. It + * provides a rudimentary mechanism to signal when values have changed. + */ +public final class ServiceConfigAccessor { + + private static final Set<String> SERVER_FLAGS_KEYS_TO_WATCH = Collections.unmodifiableSet( + new ArraySet<>(new String[] { + ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED, + ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT, + ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE, + ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE, + ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE, + ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS, + ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS, + ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS + })); + + // TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented + private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(1); + // TODO(b/179488561): Put this back to 1 minute when primary provider is fully implemented + private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ = + Duration.ofSeconds(20); + private static final Duration DEFAULT_PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5); + + private static final Object SLOCK = new Object(); + + /** The singleton instance. Initialized once in {@link #getInstance(Context)}. */ + @GuardedBy("SLOCK") + @Nullable + private static ServiceConfigAccessor sInstance; + + @NonNull private final Context mContext; + + /** + * An ultimate "feature switch" for location-based time zone detection. If this is + * {@code false}, the device cannot support the feature without a config change or a reboot: + * This affects what services are started on boot to minimize expense when the feature is not + * wanted. + */ + private final boolean mGeoDetectionFeatureSupportedInConfig; + + @NonNull private final ServerFlags mServerFlags; + + private ServiceConfigAccessor(@NonNull Context context) { + mContext = Objects.requireNonNull(context); + + // The config value is expected to be the main feature flag. Platform developers can also + // force enable the feature using a persistent system property. Because system properties + // can change, this value is cached and only changes on reboot. + mGeoDetectionFeatureSupportedInConfig = context.getResources().getBoolean( + com.android.internal.R.bool.config_enableGeolocationTimeZoneDetection) + || SystemProperties.getBoolean( + "persist.sys.location_time_zone_detection_feature_supported", false); + + mServerFlags = ServerFlags.getInstance(mContext); + } + + /** Returns the singleton instance. */ + public static ServiceConfigAccessor getInstance(Context context) { + synchronized (SLOCK) { + if (sInstance == null) { + sInstance = new ServiceConfigAccessor(context); + } + return sInstance; + } + } + + /** + * Adds a listener that will be called server flags related to this class change. The callbacks + * are delivered on the main looper thread. + * + * <p>Note: Only for use by long-lived objects. There is deliberately no associated remove + * method. + */ + public void addListener(@NonNull ConfigurationChangeListener listener) { + mServerFlags.addListener(listener, SERVER_FLAGS_KEYS_TO_WATCH); + } + + /** Returns {@code true} if any form of automatic time zone detection is supported. */ + public boolean isAutoDetectionFeatureSupported() { + return deviceHasTelephonyNetwork() || isGeoTimeZoneDetectionFeatureSupported(); + } + + private boolean deviceHasTelephonyNetwork() { + // TODO b/150583524 Avoid the use of a deprecated API. + return mContext.getSystemService(ConnectivityManager.class) + .isNetworkSupported(ConnectivityManager.TYPE_MOBILE); + } + + /** + * Returns {@code true} if the location-based time zone detection feature can be supported on + * this device at all according to config. When {@code false}, implies that various other + * location-based settings will be turned off or rendered meaningless. Typically {@link + * #isGeoTimeZoneDetectionFeatureSupported()} should be used instead. + */ + public boolean isGeoTimeZoneDetectionFeatureSupportedInConfig() { + return mGeoDetectionFeatureSupportedInConfig; + } + + /** + * Returns {@code true} if the location-based time zone detection feature is supported on the + * device. This can be used during feature testing on builds that are capable of location time + * zone detection to enable / disable the feature for some users. + */ + public boolean isGeoTimeZoneDetectionFeatureSupported() { + return mGeoDetectionFeatureSupportedInConfig + && isGeoTimeZoneDetectionFeatureSupportedInternal(); + } + + private boolean isGeoTimeZoneDetectionFeatureSupportedInternal() { + final boolean defaultEnabled = true; + return mServerFlags.getBoolean( + ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED, + defaultEnabled); + } + + /** + * Returns {@code true} if the primary location time zone provider can be used. + */ + public boolean isPrimaryLocationTimeZoneProviderEnabled() { + return getPrimaryLocationTimeZoneProviderEnabledOverride() + .orElse(isPrimaryLocationTimeZoneProviderEnabledInConfig()); + } + + private boolean isPrimaryLocationTimeZoneProviderEnabledInConfig() { + int providerEnabledConfigId = R.bool.config_enablePrimaryLocationTimeZoneProvider; + return getConfigBoolean(providerEnabledConfigId); + } + + @NonNull + private Optional<Boolean> getPrimaryLocationTimeZoneProviderEnabledOverride() { + return mServerFlags.getOptionalBoolean( + ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE); + } + + /** + * Returns {@code true} if the secondary location time zone provider can be used. + */ + public boolean isSecondaryLocationTimeZoneProviderEnabled() { + return getSecondaryLocationTimeZoneProviderEnabledOverride() + .orElse(isSecondaryLocationTimeZoneProviderEnabledInConfig()); + } + + private boolean isSecondaryLocationTimeZoneProviderEnabledInConfig() { + int providerEnabledConfigId = R.bool.config_enableSecondaryLocationTimeZoneProvider; + return getConfigBoolean(providerEnabledConfigId); + } + + @NonNull + private Optional<Boolean> getSecondaryLocationTimeZoneProviderEnabledOverride() { + return mServerFlags.getOptionalBoolean( + ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE); + } + + /** + * Returns whether location time zone detection is enabled for users when there's no setting + * value. Intended for use during feature release testing to "opt-in" users that haven't shown + * an explicit preference. + */ + public boolean isGeoDetectionEnabledForUsersByDefault() { + return mServerFlags.getBoolean( + ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT, false); + } + + /** + * Returns whether location time zone detection is force enabled/disabled for users. Intended + * for use during feature release testing to force a given state. + */ + @NonNull + public Optional<Boolean> getGeoDetectionSettingEnabledOverride() { + return mServerFlags.getOptionalBoolean( + ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE); + } + + /** + * Returns the time to send to a location time zone provider that informs it how long it has + * to return its first time zone suggestion. + */ + @NonNull + public Duration getLocationTimeZoneProviderInitializationTimeout() { + return mServerFlags.getDurationFromMillis( + ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS, + DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT); + } + + /** + * Returns the time added to {@link #getLocationTimeZoneProviderInitializationTimeout()} by the + * server before unilaterally declaring the provider is uncertain. + */ + @NonNull + public Duration getLocationTimeZoneProviderInitializationTimeoutFuzz() { + return mServerFlags.getDurationFromMillis( + ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS, + DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ); + } + + /** + * Returns the time after uncertainty is detected by providers before the location time zone + * manager makes a suggestion to the time zone detector. + */ + @NonNull + public Duration getLocationTimeZoneUncertaintyDelay() { + return mServerFlags.getDurationFromMillis( + ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS, + DEFAULT_PROVIDER_UNCERTAINTY_DELAY); + } + + private boolean getConfigBoolean(int providerEnabledConfigId) { + Resources resources = mContext.getResources(); + return resources.getBoolean(providerEnabledConfigId); + } +} diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java index 203a8a4e02cc..cd220b164851 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java @@ -27,16 +27,21 @@ import android.annotation.NonNull; */ public interface TimeZoneDetectorInternal extends Dumpable.Container { - /** Adds a listener that will be invoked when time zone detection configuration is changed. */ - void addConfigurationListener(ConfigurationChangeListener listener); + /** Adds a listener that will be invoked when {@link ConfigurationInternal} may have changed. */ + void addConfigurationListener(@NonNull ConfigurationChangeListener listener); /** * Removes a listener previously added via {@link * #addConfigurationListener(ConfigurationChangeListener)}. */ - void removeConfigurationListener(ConfigurationChangeListener listener); + void removeConfigurationListener(@NonNull ConfigurationChangeListener listener); - /** Returns the {@link ConfigurationInternal} for the current user. */ + /** + * Returns a snapshot of the {@link ConfigurationInternal} for the current user. This is only a + * snapshot so callers must use {@link #addConfigurationListener(ConfigurationChangeListener)} + * to be notified when it changes. + */ + @NonNull ConfigurationInternal getCurrentUserConfigurationInternal(); /** diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java index bd71ddf67094..c20400ae7a4b 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java @@ -33,7 +33,6 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; -import android.os.SystemProperties; import android.util.ArrayMap; import android.util.IndentingPrintWriter; import android.util.Slog; @@ -62,27 +61,6 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub static final String TAG = "time_zone_detector"; /** - * A "feature switch" for location-based time zone detection. If this is {@code false}. It is - * initialized and never refreshed; it affects what services are started on boot so consistency - * is important. - */ - @Nullable - private static Boolean sGeoLocationTimeZoneDetectionSupported; - - /** Returns {@code true} if the location-based time zone detection feature is enabled. */ - public static boolean isGeoLocationTimeZoneDetectionSupported(Context context) { - if (sGeoLocationTimeZoneDetectionSupported == null) { - // The config value is expected to be the main switch. Platform developers can also - // enable the feature using a persistent system property. - sGeoLocationTimeZoneDetectionSupported = context.getResources().getBoolean( - com.android.internal.R.bool.config_enableGeolocationTimeZoneDetection) - || SystemProperties.getBoolean( - "persist.sys.location_time_zone_detection_feature_enabled", false); - } - return sGeoLocationTimeZoneDetectionSupported; - } - - /** * Handles the service lifecycle for {@link TimeZoneDetectorService} and * {@link TimeZoneDetectorInternalImpl}. */ @@ -98,11 +76,10 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub Context context = getContext(); Handler handler = FgThread.getHandler(); - boolean geolocationTimeZoneDetectionSupported = - isGeoLocationTimeZoneDetectionSupported(context); + ServiceConfigAccessor serviceConfigAccessor = + ServiceConfigAccessor.getInstance(context); TimeZoneDetectorStrategy timeZoneDetectorStrategy = - TimeZoneDetectorStrategyImpl.create( - context, handler, geolocationTimeZoneDetectionSupported); + TimeZoneDetectorStrategyImpl.create(context, handler, serviceConfigAccessor); // Create and publish the local service for use by internal callers. TimeZoneDetectorInternal internal = @@ -330,7 +307,8 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub boolean isGeoTimeZoneDetectionSupported() { enforceManageTimeZoneDetectorPermission(); - return isGeoLocationTimeZoneDetectionSupported(mContext); + return ServiceConfigAccessor.getInstance(mContext) + .isGeoTimeZoneDetectionFeatureSupported(); } @Override diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java index 0b1d6d71ea7b..8266f121822e 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java @@ -75,17 +75,21 @@ import android.util.IndentingPrintWriter; public interface TimeZoneDetectorStrategy extends Dumpable, Dumpable.Container { /** - * Sets a listener that will be triggered whenever time zone detection configuration is + * Adds a listener that will be triggered whenever {@link ConfigurationInternal} may have * changed. */ void addConfigChangeListener(@NonNull ConfigurationChangeListener listener); - /** Returns the user's time zone configuration. */ + /** + * Returns a snapshot of the configuration that controls time zone detector behavior for the + * specified user. + */ @NonNull ConfigurationInternal getConfigurationInternal(@UserIdInt int userId); /** - * Returns the configuration that controls time zone detector behavior for the current user. + * Returns a snapshot of the configuration that controls time zone detector behavior for the + * current user. */ @NonNull ConfigurationInternal getCurrentUserConfigurationInternal(); diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java index c464b74ca726..d163a0e22320 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java @@ -38,7 +38,6 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.timedetector.DeviceConfig; import java.util.ArrayList; import java.util.List; @@ -204,11 +203,9 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat */ public static TimeZoneDetectorStrategyImpl create( @NonNull Context context, @NonNull Handler handler, - boolean geoDetectionSupported) { + @NonNull ServiceConfigAccessor serviceConfigAccessor) { - DeviceConfig deviceConfig = new DeviceConfig(); - EnvironmentImpl environment = new EnvironmentImpl( - context, handler, deviceConfig, geoDetectionSupported); + Environment environment = new EnvironmentImpl(context, handler, serviceConfigAccessor); return new TimeZoneDetectorStrategyImpl(environment); } diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java index e463ee22452d..98e984d2c3ac 100644 --- a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java +++ b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java @@ -19,9 +19,9 @@ package com.android.server.timezonedetector.location; import android.annotation.NonNull; import com.android.server.LocalServices; -import com.android.server.timedetector.DeviceConfig; import com.android.server.timezonedetector.ConfigurationChangeListener; import com.android.server.timezonedetector.ConfigurationInternal; +import com.android.server.timezonedetector.ServiceConfigAccessor; import com.android.server.timezonedetector.TimeZoneDetectorInternal; import java.time.Duration; @@ -33,28 +33,19 @@ import java.util.Objects; */ class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Environment { - // TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented - private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(1); - // TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented - private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ = - Duration.ofSeconds(20); - private static final Duration DEFAULT_PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5); - @NonNull private final TimeZoneDetectorInternal mTimeZoneDetectorInternal; - @NonNull private final LocationTimeZoneProviderController mController; - @NonNull private final DeviceConfig mDeviceConfig; + @NonNull private final ServiceConfigAccessor mServiceConfigAccessor; @NonNull private final ConfigurationChangeListener mConfigurationChangeListener; ControllerEnvironmentImpl(@NonNull ThreadingDomain threadingDomain, - @NonNull DeviceConfig deviceConfig, + @NonNull ServiceConfigAccessor serviceConfigAccessor, @NonNull LocationTimeZoneProviderController controller) { super(threadingDomain); - mController = Objects.requireNonNull(controller); - mDeviceConfig = Objects.requireNonNull(deviceConfig); + mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor); mTimeZoneDetectorInternal = LocalServices.getService(TimeZoneDetectorInternal.class); // Listen for configuration changes. - mConfigurationChangeListener = () -> mThreadingDomain.post(mController::onConfigChanged); + mConfigurationChangeListener = () -> mThreadingDomain.post(controller::onConfigChanged); mTimeZoneDetectorInternal.addConfigurationListener(mConfigurationChangeListener); } @@ -73,24 +64,18 @@ class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Envir @Override @NonNull Duration getProviderInitializationTimeout() { - return mDeviceConfig.getDurationFromMillis( - DeviceConfig.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS, - DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT); + return mServiceConfigAccessor.getLocationTimeZoneProviderInitializationTimeout(); } @Override @NonNull Duration getProviderInitializationTimeoutFuzz() { - return mDeviceConfig.getDurationFromMillis( - DeviceConfig.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS, - DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ); + return mServiceConfigAccessor.getLocationTimeZoneProviderInitializationTimeoutFuzz(); } @Override @NonNull Duration getUncertaintyDelay() { - return mDeviceConfig.getDurationFromMillis( - DeviceConfig.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS, - DEFAULT_PROVIDER_UNCERTAINTY_DELAY); + return mServiceConfigAccessor.getLocationTimeZoneUncertaintyDelay(); } } diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java index 364eaf8dac04..0d1692a8781d 100644 --- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java +++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java @@ -21,11 +21,11 @@ import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_DI import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_NONE; import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_SIMULATED; import static android.app.time.LocationTimeZoneManager.SECONDARY_PROVIDER_NAME; +import static android.app.time.LocationTimeZoneManager.SERVICE_NAME; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.content.res.Resources; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -43,9 +43,8 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; import com.android.server.FgThread; import com.android.server.SystemService; -import com.android.server.timedetector.DeviceConfig; +import com.android.server.timezonedetector.ServiceConfigAccessor; import com.android.server.timezonedetector.TimeZoneDetectorInternal; -import com.android.server.timezonedetector.TimeZoneDetectorService; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -96,28 +95,31 @@ public class LocationTimeZoneManagerService extends Binder { private LocationTimeZoneManagerService mService; + @NonNull + private final ServiceConfigAccessor mServerConfigAccessor; + public Lifecycle(@NonNull Context context) { super(Objects.requireNonNull(context)); + mServerConfigAccessor = ServiceConfigAccessor.getInstance(context); } @Override public void onStart() { Context context = getContext(); - if (TimeZoneDetectorService.isGeoLocationTimeZoneDetectionSupported(context)) { + if (mServerConfigAccessor.isGeoTimeZoneDetectionFeatureSupportedInConfig()) { mService = new LocationTimeZoneManagerService(context); // The service currently exposes no LocalService or Binder API, but it extends // Binder and is registered as a binder service so it can receive shell commands. - publishBinderService("location_time_zone_manager", mService); + publishBinderService(SERVICE_NAME, mService); } else { - Slog.i(TAG, getClass() + " is disabled"); + Slog.d(TAG, "Geo time zone detection feature is disabled in config"); } } @Override - public void onBootPhase(int phase) { - Context context = getContext(); - if (TimeZoneDetectorService.isGeoLocationTimeZoneDetectionSupported(context)) { + public void onBootPhase(@BootPhase int phase) { + if (mServerConfigAccessor.isGeoTimeZoneDetectionFeatureSupportedInConfig()) { if (phase == PHASE_SYSTEM_SERVICES_READY) { // The location service must be functioning after this boot phase. mService.onSystemReady(); @@ -159,6 +161,9 @@ public class LocationTimeZoneManagerService extends Binder { /** The shared lock from {@link #mThreadingDomain}. */ @NonNull private final Object mSharedLock; + @NonNull + private final ServiceConfigAccessor mServiceConfigAccessor; + // Lazily initialized. Can be null if the service has been stopped. @GuardedBy("mSharedLock") private ControllerImpl mLocationTimeZoneDetectorController; @@ -180,24 +185,38 @@ public class LocationTimeZoneManagerService extends Binder { mHandler = FgThread.getHandler(); mThreadingDomain = new HandlerThreadingDomain(mHandler); mSharedLock = mThreadingDomain.getLockObject(); + mServiceConfigAccessor = ServiceConfigAccessor.getInstance(mContext); } + // According to the SystemService docs: All lifecycle methods are called from the system + // server's main looper thread. void onSystemReady() { - // Called on an arbitrary thread during initialization. - synchronized (mSharedLock) { - // TODO(b/152744911): LocationManagerService watches for packages disappearing. Need to - // do anything here? + mServiceConfigAccessor.addListener(this::handleServiceConfigurationChangedOnMainThread); + } - // TODO(b/152744911): LocationManagerService watches for foreground app changes. Need to - // do anything here? - // TODO(b/152744911): LocationManagerService watches screen state. Need to do anything - // here? + private void handleServiceConfigurationChangedOnMainThread() { + // This method is called on the main thread, but service logic takes place on the threading + // domain thread, so we post the work there. + + // The way all service-level configuration changes are handled is to just restart this + // service - this is simple and effective, and service configuration changes should be rare. + mThreadingDomain.post(this::restartIfRequiredOnDomainThread); + } + + private void restartIfRequiredOnDomainThread() { + mThreadingDomain.assertCurrentThread(); + + synchronized (mSharedLock) { + // Stop and start the service, waiting until completion. + stopOnDomainThread(); + startOnDomainThread(); } } + // According to the SystemService docs: All lifecycle methods are called from the system + // server's main looper thread. void onSystemThirdPartyAppsCanStart() { - // Called on an arbitrary thread during initialization. We do not want to wait for - // completion as it would delay boot. + // Do not wait for completion as it would delay boot. final boolean waitForCompletion = false; startInternal(waitForCompletion); } @@ -205,6 +224,9 @@ public class LocationTimeZoneManagerService extends Binder { /** * Starts the service during server initialization or during tests after a call to * {@link #stop()}. + * + * <p>Because this method posts work to the {@code mThreadingDomain} thread and waits for + * completion, it cannot be called from the {@code mThreadingDomain} thread. */ void start() { enforceManageTimeZoneDetectorPermission(); @@ -214,28 +236,17 @@ public class LocationTimeZoneManagerService extends Binder { } /** - * Starts the service during server initialization or during tests after a call to - * {@link #stop()}. + * Starts the service during server initialization, if the configuration changes or during tests + * after a call to {@link #stop()}. * * <p>To avoid tests needing to sleep, when {@code waitForCompletion} is {@code true}, this * method will not return until all the system server components have started. + * + * <p>Because this method posts work to the {@code mThreadingDomain} thread, it cannot be + * called from the {@code mThreadingDomain} thread when {@code waitForCompletion} is true. */ private void startInternal(boolean waitForCompletion) { - Runnable runnable = () -> { - synchronized (mSharedLock) { - if (mLocationTimeZoneDetectorController == null) { - LocationTimeZoneProvider primary = createPrimaryProvider(); - LocationTimeZoneProvider secondary = createSecondaryProvider(); - mLocationTimeZoneDetectorController = - new ControllerImpl(mThreadingDomain, primary, secondary); - DeviceConfig deviceConfig = new DeviceConfig(); - mEnvironment = new ControllerEnvironmentImpl( - mThreadingDomain, deviceConfig, mLocationTimeZoneDetectorController); - ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain); - mLocationTimeZoneDetectorController.initialize(mEnvironment, callback); - } - } - }; + Runnable runnable = this::startOnDomainThread; if (waitForCompletion) { mThreadingDomain.postAndWait(runnable, BLOCKING_OP_WAIT_DURATION_MILLIS); } else { @@ -243,11 +254,38 @@ public class LocationTimeZoneManagerService extends Binder { } } + private void startOnDomainThread() { + mThreadingDomain.assertCurrentThread(); + + synchronized (mSharedLock) { + if (!mServiceConfigAccessor.isGeoTimeZoneDetectionFeatureSupported()) { + debugLog("Not starting " + SERVICE_NAME + ": it is disabled in service config"); + return; + } + + if (mLocationTimeZoneDetectorController == null) { + LocationTimeZoneProvider primary = createPrimaryProvider(); + LocationTimeZoneProvider secondary = createSecondaryProvider(); + + ControllerImpl controller = + new ControllerImpl(mThreadingDomain, primary, secondary); + ControllerEnvironmentImpl environment = new ControllerEnvironmentImpl( + mThreadingDomain, mServiceConfigAccessor, controller); + ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain); + controller.initialize(environment, callback); + + mEnvironment = environment; + mLocationTimeZoneDetectorController = controller; + } + } + } + + @NonNull private LocationTimeZoneProvider createPrimaryProvider() { LocationTimeZoneProviderProxy proxy; if (isProviderInSimulationMode(PRIMARY_PROVIDER_NAME)) { proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain); - } else if (isProviderDisabled(PRIMARY_PROVIDER_NAME)) { + } else if (!isProviderEnabled(PRIMARY_PROVIDER_NAME)) { proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain); } else { proxy = new RealLocationTimeZoneProviderProxy( @@ -262,11 +300,12 @@ public class LocationTimeZoneManagerService extends Binder { return new BinderLocationTimeZoneProvider(mThreadingDomain, PRIMARY_PROVIDER_NAME, proxy); } + @NonNull private LocationTimeZoneProvider createSecondaryProvider() { LocationTimeZoneProviderProxy proxy; if (isProviderInSimulationMode(SECONDARY_PROVIDER_NAME)) { proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain); - } else if (isProviderDisabled(SECONDARY_PROVIDER_NAME)) { + } else if (!isProviderEnabled(SECONDARY_PROVIDER_NAME)) { proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain); } else { proxy = new RealLocationTimeZoneProviderProxy( @@ -282,33 +321,27 @@ public class LocationTimeZoneManagerService extends Binder { } /** Used for bug triage and in tests to simulate provider events. */ - private boolean isProviderInSimulationMode(String providerName) { + private boolean isProviderInSimulationMode(@NonNull String providerName) { return isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_SIMULATED); } - /** Used for bug triage, tests and experiments to remove a provider. */ - private boolean isProviderDisabled(String providerName) { - return !isProviderEnabledInConfig(providerName) - || isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_DISABLED); - } + /** Used for bug triage, and by tests and experiments to remove a provider. */ + private boolean isProviderEnabled(@NonNull String providerName) { + if (isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_DISABLED)) { + return false; + } - private boolean isProviderEnabledInConfig(String providerName) { - int providerEnabledConfigId; switch (providerName) { case PRIMARY_PROVIDER_NAME: { - providerEnabledConfigId = R.bool.config_enablePrimaryLocationTimeZoneProvider; - break; + return mServiceConfigAccessor.isPrimaryLocationTimeZoneProviderEnabled(); } case SECONDARY_PROVIDER_NAME: { - providerEnabledConfigId = R.bool.config_enableSecondaryLocationTimeZoneProvider; - break; + return mServiceConfigAccessor.isSecondaryLocationTimeZoneProviderEnabled(); } default: { throw new IllegalArgumentException(providerName); } } - Resources resources = mContext.getResources(); - return resources.getBoolean(providerEnabledConfigId); } private boolean isProviderModeOverrideSet(@NonNull String providerName, @NonNull String mode) { @@ -326,22 +359,29 @@ public class LocationTimeZoneManagerService extends Binder { } /** - * Stops the service for tests. To avoid tests needing to sleep, this method will not return - * until all the system server components have stopped. + * Stops the service for tests and other rare cases. To avoid tests needing to sleep, this + * method will not return until all the system server components have stopped. + * + * <p>Because this method posts work to the {@code mThreadingDomain} thread and waits it cannot + * be called from the {@code mThreadingDomain} thread. */ void stop() { enforceManageTimeZoneDetectorPermission(); - mThreadingDomain.postAndWait(() -> { - synchronized (mSharedLock) { - if (mLocationTimeZoneDetectorController != null) { - mLocationTimeZoneDetectorController.destroy(); - mLocationTimeZoneDetectorController = null; - mEnvironment.destroy(); - mEnvironment = null; - } + mThreadingDomain.postAndWait(this::stopOnDomainThread, BLOCKING_OP_WAIT_DURATION_MILLIS); + } + + private void stopOnDomainThread() { + mThreadingDomain.assertCurrentThread(); + + synchronized (mSharedLock) { + if (mLocationTimeZoneDetectorController != null) { + mLocationTimeZoneDetectorController.destroy(); + mLocationTimeZoneDetectorController = null; + mEnvironment.destroy(); + mEnvironment = null; } - }, BLOCKING_OP_WAIT_DURATION_MILLIS); + } } @Override diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java index b53150c729bc..bdf4a70a6a2b 100644 --- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java +++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java @@ -21,6 +21,7 @@ import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_DI import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_NONE; import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_SIMULATED; import static android.app.time.LocationTimeZoneManager.SECONDARY_PROVIDER_NAME; +import static android.app.time.LocationTimeZoneManager.SERVICE_NAME; import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_DUMP_STATE; import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_RECORD_PROVIDER_STATES; import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND; @@ -102,7 +103,7 @@ class LocationTimeZoneManagerShellCommand extends ShellCommand { @Override public void onHelp() { final PrintWriter pw = getOutPrintWriter(); - pw.println("Location Time Zone Manager (location_time_zone_manager) commands for tests:"); + pw.printf("Location Time Zone Manager (%s) commands for tests:\n", SERVICE_NAME); pw.println(" help"); pw.println(" Print this help text."); pw.printf(" %s\n", SHELL_COMMAND_START); diff --git a/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java index 38211efc1c63..531c62c5b4e9 100644 --- a/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java +++ b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java @@ -25,7 +25,6 @@ import static com.android.server.timezonedetector.location.LocationTimeZoneManag import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -41,6 +40,7 @@ import android.util.IndentingPrintWriter; import com.android.internal.annotations.GuardedBy; import com.android.server.ServiceWatcher; +import com.android.server.ServiceWatcher.BoundService; import java.util.Objects; import java.util.function.Predicate; @@ -123,7 +123,7 @@ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy { return resolves; } - private void onBind(IBinder binder, ComponentName componentName) { + private void onBind(IBinder binder, BoundService boundService) { mThreadingDomain.assertCurrentThread(); synchronized (mSharedLock) { diff --git a/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java index 14820319d9df..e8386bc22403 100644 --- a/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java +++ b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java @@ -63,8 +63,6 @@ final class TimeZoneProviderRequest { return mSendUpdates; } - // TODO(b/152744911) - once there are a couple of implementations, decide whether this needs to - // be passed to the TimeZoneProviderService and remove if it is not useful. /** * Returns the maximum time that the provider is allowed to initialize before it is expected to * send an event of any sort. Only valid when {@link #sendUpdates()} is {@code true}. Failure to 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/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 60ca725b118e..6ce9048d79e2 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1708,7 +1708,7 @@ class ActivityStarter { // If the activity being launched is the same as the one currently at the top, then // we need to check if it should only be launched once. - final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask(); + final Task topRootTask = mPreferredTaskDisplayArea.getFocusedRootTask(); if (topRootTask != null) { startResult = deliverToCurrentTopIfNeeded(topRootTask, intentGrants); if (startResult != START_SUCCESS) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index b803fc37a421..af032c108538 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -2095,10 +2095,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - /** @return the max ANR delay from all registered {@link AnrController} instances */ - public long getMaxAnrDelayMillis(ApplicationInfo info) { + /** + * @return the controller with the max ANR delay from all registered + * {@link AnrController} instances + */ + @Nullable + public AnrController getAnrController(ApplicationInfo info) { if (info == null || info.packageName == null) { - return 0; + return null; } final ArrayList<AnrController> controllers; @@ -2107,12 +2111,19 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } final String packageName = info.packageName; + final int uid = info.uid; long maxDelayMs = 0; + AnrController controllerWithMaxDelay = null; + for (AnrController controller : controllers) { - maxDelayMs = Math.max(maxDelayMs, controller.getAnrDelayMillis(packageName, info.uid)); + long delayMs = controller.getAnrDelayMillis(packageName, uid); + if (delayMs > 0 && delayMs > maxDelayMs) { + controllerWithMaxDelay = controller; + maxDelayMs = delayMs; + } } - maxDelayMs = Math.max(maxDelayMs, 0); - return maxDelayMs; + + return controllerWithMaxDelay; } @Override 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 d5ded97ee0b9..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; } 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/FactoryErrorDialog.java b/services/core/java/com/android/server/wm/FactoryErrorDialog.java index 88b5475cc3a2..afdf1ee4de7f 100644 --- a/services/core/java/com/android/server/wm/FactoryErrorDialog.java +++ b/services/core/java/com/android/server/wm/FactoryErrorDialog.java @@ -41,6 +41,11 @@ final class FactoryErrorDialog extends BaseErrorDialog { public void onStop() { } + @Override + protected void closeDialog() { + /* Do nothing */ + } + private final Handler mHandler = new Handler() { public void handleMessage(Message msg) { throw new RuntimeException("Rebooting from failed factory test"); 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/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 89d30408fe25..c0ccd81c9b15 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; @@ -253,6 +253,7 @@ import android.view.MagnificationSpec; import android.view.MotionEvent; import android.view.PointerIcon; import android.view.RemoteAnimationAdapter; +import android.view.ScrollCaptureResponse; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceSession; @@ -2393,15 +2394,9 @@ public class WindowManagerService extends IWindowManager.Stub } } - // We may be deferring layout passes at the moment, but since the client is interested - // in the new out values right now we need to force a layout. - mWindowPlacerLocked.performSurfacePlacement(true /* force */); - + // Create surfaceControl before surface placement otherwise layout will be skipped + // (because WS.isGoneForLayout() is true when there is no surface. if (shouldRelayout) { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1"); - - result = win.relayoutVisibleWindow(result); - try { result = createSurfaceControl(outSurfaceControl, result, win, winAnimator); } catch (Exception e) { @@ -2413,6 +2408,17 @@ public class WindowManagerService extends IWindowManager.Stub Binder.restoreCallingIdentity(origId); return 0; } + } + + // We may be deferring layout passes at the moment, but since the client is interested + // in the new out values right now we need to force a layout. + mWindowPlacerLocked.performSurfacePlacement(true /* force */); + + if (shouldRelayout) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1"); + + result = win.relayoutVisibleWindow(result); + if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { focusMayChange = true; } @@ -2981,7 +2987,7 @@ public class WindowManagerService extends IWindowManager.Stub } boolean isValidPictureInPictureAspectRatio(DisplayContent displayContent, float aspectRatio) { - return displayContent.getPinnedStackController().isValidPictureInPictureAspectRatio( + return displayContent.getPinnedTaskController().isValidPictureInPictureAspectRatio( aspectRatio); } @@ -6880,7 +6886,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 +6911,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 +6921,7 @@ public class WindowManagerService extends IWindowManager.Stub } synchronized (mGlobalLock) { final DisplayContent displayContent = mRoot.getDisplayContent(displayId); - displayContent.getPinnedStackController().registerPinnedStackListener(listener); + displayContent.getPinnedTaskController().registerPinnedTaskListener(listener); } } @@ -7155,12 +7160,14 @@ public class WindowManagerService extends IWindowManager.Stub } final long token = Binder.clearCallingIdentity(); try { + ScrollCaptureResponse.Builder responseBuilder = new ScrollCaptureResponse.Builder(); synchronized (mGlobalLock) { DisplayContent dc = mRoot.getDisplayContent(displayId); if (dc == null) { ProtoLog.e(WM_ERROR, "Invalid displayId for requestScrollCapture: %d", displayId); - callbacks.onUnavailable(); + responseBuilder.setDescription(String.format("bad displayId: %d", displayId)); + callbacks.onScrollCaptureResponse(responseBuilder.build()); return; } WindowState topWindow = null; @@ -7169,17 +7176,20 @@ public class WindowManagerService extends IWindowManager.Stub } WindowState targetWindow = dc.findScrollCaptureTargetWindow(topWindow, taskId); if (targetWindow == null) { - callbacks.onUnavailable(); + responseBuilder.setDescription("findScrollCaptureTargetWindow returned null"); + callbacks.onScrollCaptureResponse(responseBuilder.build()); return; } - // Forward to the window for handling. try { + // Forward to the window for handling, which will respond using the callback. targetWindow.mClient.requestScrollCapture(callbacks); } catch (RemoteException e) { ProtoLog.w(WM_ERROR, "requestScrollCapture: caught exception dispatching to window." + "token=%s", targetWindow.mClient.asBinder()); - callbacks.onUnavailable(); + responseBuilder.setWindowTitle(targetWindow.getName()); + responseBuilder.setDescription(String.format("caught exception: %s", e)); + callbacks.onScrollCaptureResponse(responseBuilder.build()); } } } catch (RemoteException e) { 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 60e95e5cfd6e..9ae5beb625f8 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1224,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; } } diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp index f054e7c73015..8c6d084fba99 100644 --- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp +++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp @@ -232,21 +232,6 @@ static void com_android_server_am_CachedAppOptimizer_compactProcess(JNIEnv*, job compactProcessOrFallback(pid, compactionFlags); } -static void com_android_server_am_CachedAppOptimizer_enableFreezerInternal( - JNIEnv *env, jobject clazz, jboolean enable) { - bool success = true; - - if (enable) { - success = SetTaskProfiles(0, {"FreezerEnabled"}, true); - } else { - success = SetTaskProfiles(0, {"FreezerDisabled"}, true); - } - - if (!success) { - jniThrowException(env, "java/lang/RuntimeException", "Unknown error"); - } -} - static void com_android_server_am_CachedAppOptimizer_freezeBinder( JNIEnv *env, jobject clazz, jint pid, jboolean freeze) { @@ -280,15 +265,19 @@ static jint com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo(JNIEnv static jstring com_android_server_am_CachedAppOptimizer_getFreezerCheckPath(JNIEnv* env, jobject clazz) { - return env->NewStringUTF(CGROUP_FREEZE_PATH); + std::string path; + + if (!getAttributePathForTask("FreezerState", getpid(), &path)) { + path = ""; + } + + return env->NewStringUTF(path.c_str()); } static const JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem}, {"compactProcess", "(II)V", (void*)com_android_server_am_CachedAppOptimizer_compactProcess}, - {"enableFreezerInternal", "(Z)V", - (void*)com_android_server_am_CachedAppOptimizer_enableFreezerInternal}, {"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder}, {"getBinderFreezeInfo", "(I)I", (void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo}, diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 21d57d8c189f..10705af9ac38 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -331,7 +331,7 @@ public: uint32_t policyFlags) override; bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) override; - void pokeUserActivity(nsecs_t eventTime, int32_t eventType) override; + void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) override; bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid, int32_t injectorUid) override; void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) override; void setPointerCapture(bool enabled) override; @@ -1325,9 +1325,9 @@ bool NativeInputManager::dispatchUnhandledKey(const sp<IBinder>& token, return result; } -void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) { +void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) { ATRACE_CALL(); - android_server_PowerManagerService_userActivity(eventTime, eventType); + android_server_PowerManagerService_userActivity(eventTime, eventType, displayId); } bool NativeInputManager::checkInjectEventsPermissionNonReentrant( diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp index 63a6eedd9e66..9b7e27d891c4 100644 --- a/services/core/jni/com_android_server_power_PowerManagerService.cpp +++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp @@ -103,7 +103,8 @@ static bool setPowerMode(Mode mode, bool enabled) { return result == power::HalResult::SUCCESSFUL; } -void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) { +void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType, + int32_t displayId) { if (gPowerManagerServiceObj) { // Throttle calls into user activity by event type. // We're a little conservative about argument checking here in case the caller @@ -127,7 +128,7 @@ void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t env->CallVoidMethod(gPowerManagerServiceObj, gPowerManagerServiceClassInfo.userActivityFromNative, - nanoseconds_to_milliseconds(eventTime), eventType, 0); + nanoseconds_to_milliseconds(eventTime), eventType, displayId, 0); checkAndClearExceptionFromCallback(env, "userActivityFromNative"); } } @@ -285,7 +286,7 @@ int register_android_server_PowerManagerService(JNIEnv* env) { FIND_CLASS(clazz, "com/android/server/power/PowerManagerService"); GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivityFromNative, clazz, - "userActivityFromNative", "(JII)V"); + "userActivityFromNative", "(JIII)V"); // Initialize for (int i = 0; i <= USER_ACTIVITY_EVENT_LAST; i++) { diff --git a/services/core/jni/com_android_server_power_PowerManagerService.h b/services/core/jni/com_android_server_power_PowerManagerService.h index a17fd650522b..a2f335c74870 100644 --- a/services/core/jni/com_android_server_power_PowerManagerService.h +++ b/services/core/jni/com_android_server_power_PowerManagerService.h @@ -24,7 +24,8 @@ namespace android { -extern void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType); +extern void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType, + int32_t displayId); } // namespace android 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 6857a685fe17..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; @@ -3403,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)) { @@ -7581,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, @@ -8110,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()) { @@ -12372,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); @@ -12564,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: " @@ -13508,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."); } @@ -13827,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"); } @@ -14348,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"); @@ -16752,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 d50db91acc99..a3d335340e9f 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -162,6 +162,7 @@ import com.android.server.pm.ShortcutService; import com.android.server.pm.UserManagerService; import com.android.server.pm.dex.SystemServerDexLoadReporter; import com.android.server.pm.verify.domain.DomainVerificationService; +import com.android.server.policy.AppOpsPolicy; import com.android.server.policy.PermissionPolicyService; import com.android.server.policy.PhoneWindowManager; import com.android.server.policy.role.RoleServicePlatformHelperImpl; @@ -1717,14 +1718,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)) { @@ -2661,6 +2657,14 @@ public final class SystemServer implements Dumpable { } t.traceEnd(); + t.traceBegin("RegisterAppOpsPolicy"); + try { + mActivityManagerService.setAppOpsPolicy(new AppOpsPolicy()); + } catch (Throwable e) { + reportWtf("registering app ops policy", e); + } + t.traceEnd(); + // No dependency on Webview preparation in system server. But this should // be completed before allowing 3rd party final String WEBVIEW_PREPARATION = "WebViewFactoryPreparation"; diff --git a/services/net/Android.bp b/services/net/Android.bp index c68004a3591b..b01e42516358 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -52,6 +52,7 @@ java_library { libs: [ "unsupportedappusage", "framework-wifi-util-lib", + "framework-connectivity" ], static_libs: [ // All the classes in netd_aidl_interface must be jarjar so they do not conflict with the 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/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 728b97cc3968..18184b0a82af 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java @@ -27,11 +27,14 @@ 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.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; +import android.content.res.TypedArray; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -44,6 +47,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.dx.mockito.inline.extended.StaticMockitoSession; +import com.android.internal.R; import com.android.server.LocalServices; import com.android.server.display.LocalDisplayAdapter.BacklightAdapter; import com.android.server.lights.LightsManager; @@ -98,6 +102,9 @@ public class LocalDisplayAdapterTest { @Mock private LocalDisplayAdapter.SurfaceControlProxy mSurfaceControlProxy; + private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f }; + private static final int[] BACKLIGHT_RANGE = { 1, 255 }; + private static final float[] BACKLIGHT_RANGE_ZERO_TO_ONE = { 0.0f, 1.0f }; @Before public void setUp() throws Exception { @@ -114,6 +121,18 @@ public class LocalDisplayAdapterTest { mListener, mInjector); spyOn(mAdapter); doReturn(mMockedContext).when(mAdapter).getOverlayContext(); + + TypedArray mockNitsRange = createFloatTypedArray(DISPLAY_RANGE_NITS); + when(mMockedResources.obtainTypedArray(R.array.config_screenBrightnessNits)) + .thenReturn(mockNitsRange); + when(mMockedResources.getIntArray(R.array.config_screenBrightnessBacklight)) + .thenReturn(BACKLIGHT_RANGE); + when(mMockedResources.getFloat(com.android.internal.R.dimen + .config_screenBrightnessSettingMinimumFloat)) + .thenReturn(BACKLIGHT_RANGE_ZERO_TO_ONE[0]); + when(mMockedResources.getFloat(com.android.internal.R.dimen + .config_screenBrightnessSettingMaximumFloat)) + .thenReturn(BACKLIGHT_RANGE_ZERO_TO_ONE[1]); } @After @@ -629,13 +648,13 @@ public class LocalDisplayAdapterTest { // Test as default display BacklightAdapter ba = new BacklightAdapter(displayToken, true /*isDefault*/, mSurfaceControlProxy); - ba.setBrightness(0.514f); + ba.setBacklight(0.514f); verify(mSurfaceControlProxy).setDisplayBrightness(displayToken, 0.514f); // Test as not default display BacklightAdapter ba2 = new BacklightAdapter(displayToken, false /*isDefault*/, mSurfaceControlProxy); - ba2.setBrightness(0.323f); + ba2.setBacklight(0.323f); verify(mSurfaceControlProxy).setDisplayBrightness(displayToken, 0.323f); } @@ -648,7 +667,7 @@ public class LocalDisplayAdapterTest { BacklightAdapter ba = new BacklightAdapter(displayToken, true /*isDefault*/, mSurfaceControlProxy); - ba.setBrightness(0.123f); + ba.setBacklight(0.123f); verify(mMockedBacklight).setBrightness(0.123f); } @@ -661,7 +680,7 @@ public class LocalDisplayAdapterTest { BacklightAdapter ba = new BacklightAdapter(displayToken, false /*isDefault*/, mSurfaceControlProxy); - ba.setBrightness(0.456f); + ba.setBacklight(0.456f); // Adapter does not forward any brightness in this case. verify(mMockedBacklight, never()).setBrightness(anyFloat()); @@ -864,4 +883,23 @@ public class LocalDisplayAdapterTest { } } + private TypedArray createFloatTypedArray(float[] vals) { + TypedArray mockArray = mock(TypedArray.class); + when(mockArray.length()).thenAnswer(invocation -> { + return vals.length; + }); + when(mockArray.getFloat(anyInt(), anyFloat())).thenAnswer(invocation -> { + final float def = (float) invocation.getArguments()[1]; + if (vals == null) { + return def; + } + int idx = (int) invocation.getArguments()[0]; + if (idx >= 0 && idx < vals.length) { + return vals[idx]; + } else { + return def; + } + }); + return mockArray; + } } 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 68d6557880c4..c4c9ad088e45 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 @@ -352,7 +352,8 @@ public class LocationProviderManagerTest { @Test public void testGetLastLocation_ClearOnMockRemoval() { - MockLocationProvider mockProvider = new MockLocationProvider(PROPERTIES, PROVIDER_IDENTITY); + MockLocationProvider mockProvider = new MockLocationProvider(PROPERTIES, PROVIDER_IDENTITY, + null); mockProvider.setAllowed(true); mManager.setMockProvider(mockProvider); @@ -1048,7 +1049,7 @@ public class LocationProviderManagerTest { private final ArrayList<Runnable> mFlushCallbacks = new ArrayList<>(); TestProvider(ProviderProperties properties, CallerIdentity identity) { - super(DIRECT_EXECUTOR, identity, properties); + super(DIRECT_EXECUTOR, identity, properties, null); } public void setProviderAllowed(boolean allowed) { diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java index 07170dacb4da..e8a0bb51e20f 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java @@ -71,7 +71,8 @@ public class MockableLocationProviderTest { .setPowerUsage(POWER_USAGE_LOW) .setAccuracy(ACCURACY_FINE) .build(), - CallerIdentity.forTest(0, 1, "testpackage", "test")); + CallerIdentity.forTest(0, 1, "testpackage", "test"), + null); mProvider = new MockableLocationProvider(lock); mProvider.getController().setListener(mListener); diff --git a/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java b/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java index 775bdd580157..a1eadbe4a64f 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java @@ -40,7 +40,7 @@ public class FakeProvider extends AbstractLocationProvider { private final FakeProviderInterface mFakeInterface; public FakeProvider(FakeProviderInterface fakeInterface) { - super(Runnable::run, null, null); + super(Runnable::run, null, null, null); mFakeInterface = fakeInterface; } 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/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/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/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java index 23a4c2f417c5..54825ee2745a 100644 --- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java @@ -64,7 +64,6 @@ public class AutomaticBrightnessControllerTest { @Mock HysteresisLevels mAmbientBrightnessThresholds; @Mock HysteresisLevels mScreenBrightnessThresholds; @Mock Handler mNoOpHandler; - @Mock DisplayDeviceConfig mDisplayDeviceConfig; @Mock DisplayDevice mDisplayDevice; private static final int LIGHT_SENSOR_WARMUP_TIME = 0; diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java index f0b4f1bec77b..285806b5dcd7 100644 --- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java +++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java @@ -88,7 +88,9 @@ public class BrightnessMappingStrategyTest { }; private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f }; - private static final int[] BACKLIGHT_RANGE = { 1, 255 }; + private static final float[] DISPLAY_LEVELS_RANGE_NITS = { 13.25f, 478.5f }; + private static final float[] BACKLIGHT_RANGE_ZERO_TO_ONE = { 0.0f, 1.0f }; + private static final float[] DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT = { 0.03149606299f, 1.0f }; private static final float[] EMPTY_FLOAT_ARRAY = new float[0]; private static final int[] EMPTY_INT_ARRAY = new int[0]; @@ -114,25 +116,28 @@ public class BrightnessMappingStrategyTest { }; private static final Spline GAMMA_CORRECTION_SPLINE = Spline.createSpline( new float[] { 0.0f, 100.0f, 1000.0f, 2500.0f, 4000.0f, 4900.0f, 5000.0f }, - new float[] { 0.035f, 0.035f, 0.221f, 0.523f, 0.797f, 0.980f, 1.0f }); + new float[] { 0.0475f, 0.0475f, 0.2225f, 0.5140f, 0.8056f, 0.9805f, 1.0f }); @Test public void testSimpleStrategyMappingAtControlPoints() { Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); - BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc); assertNotNull("BrightnessMappingStrategy should not be null", simple); for (int i = 0; i < LUX_LEVELS.length; i++) { - final float expectedLevel = - (float) DISPLAY_LEVELS_BACKLIGHT[i] / PowerManager.BRIGHTNESS_ON; + final float expectedLevel = MathUtils.map(PowerManager.BRIGHTNESS_OFF + 1, + PowerManager.BRIGHTNESS_ON, PowerManager.BRIGHTNESS_MIN, + PowerManager.BRIGHTNESS_MAX, DISPLAY_LEVELS_BACKLIGHT[i]); assertEquals(expectedLevel, - simple.getBrightness(LUX_LEVELS[i]), 0.01f /*tolerance*/); + simple.getBrightness(LUX_LEVELS[i]), 0.0001f /*tolerance*/); } } @Test public void testSimpleStrategyMappingBetweenControlPoints() { Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); - BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc); assertNotNull("BrightnessMappingStrategy should not be null", simple); for (int i = 1; i < LUX_LEVELS.length; i++) { final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2; @@ -146,66 +151,71 @@ public class BrightnessMappingStrategyTest { @Test public void testSimpleStrategyIgnoresNewConfiguration() { Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc); - final int N = LUX_LEVELS.length; final float[] lux = { 0f, 1f }; final float[] nits = { 0, PowerManager.BRIGHTNESS_ON }; BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits) .build(); strategy.setBrightnessConfiguration(config); - assertNotEquals(1.0f, strategy.getBrightness(1f), 0.01 /*tolerance*/); + assertNotEquals(1.0f, strategy.getBrightness(1f), 0.0001f /*tolerance*/); } @Test public void testSimpleStrategyIgnoresNullConfiguration() { Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc); strategy.setBrightnessConfiguration(null); final int N = DISPLAY_LEVELS_BACKLIGHT.length; final float expectedBrightness = (float) DISPLAY_LEVELS_BACKLIGHT[N - 1] / PowerManager.BRIGHTNESS_ON; assertEquals(expectedBrightness, - strategy.getBrightness(LUX_LEVELS[N - 1]), 0.01 /*tolerance*/); + strategy.getBrightness(LUX_LEVELS[N - 1]), 0.0001f /*tolerance*/); } @Test public void testPhysicalStrategyMappingAtControlPoints() { - Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, - DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); - BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res); + Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc); assertNotNull("BrightnessMappingStrategy should not be null", physical); for (int i = 0; i < LUX_LEVELS.length; i++) { - final float expectedLevel = DISPLAY_LEVELS_NITS[i] / DISPLAY_RANGE_NITS[1]; + final float expectedLevel = MathUtils.map(DISPLAY_RANGE_NITS[0], DISPLAY_RANGE_NITS[1], + DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT[0], + DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT[1], + DISPLAY_LEVELS_NITS[i]); assertEquals(expectedLevel, - physical.getBrightness(LUX_LEVELS[i]), 0.01f /*tolerance*/); + physical.getBrightness(LUX_LEVELS[i]), + 0.0001f /*tolerance*/); } } @Test public void testPhysicalStrategyMappingBetweenControlPoints() { - Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, - DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); - BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res); + Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS); + DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE); + BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc); assertNotNull("BrightnessMappingStrategy should not be null", physical); - Spline backlightToBrightness = - Spline.createSpline(toFloatArray(BACKLIGHT_RANGE), DISPLAY_RANGE_NITS); + Spline brightnessToNits = + Spline.createSpline(BACKLIGHT_RANGE_ZERO_TO_ONE, DISPLAY_RANGE_NITS); for (int i = 1; i < LUX_LEVELS.length; i++) { - final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2; - final float backlight = physical.getBrightness(lux) * PowerManager.BRIGHTNESS_ON; - final float nits = backlightToBrightness.interpolate(backlight); - assertTrue("Desired brightness should be between adjacent control points.", + final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2.0f; + final float brightness = physical.getBrightness(lux); + final float nits = brightnessToNits.interpolate(brightness); + assertTrue("Desired brightness should be between adjacent control points: " + nits, nits > DISPLAY_LEVELS_NITS[i - 1] && nits < DISPLAY_LEVELS_NITS[i]); } } @Test public void testPhysicalStrategyUsesNewConfigurations() { - Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, - DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); + Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc); final float[] lux = { 0f, 1f }; final float[] nits = { @@ -216,46 +226,53 @@ public class BrightnessMappingStrategyTest { BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits) .build(); strategy.setBrightnessConfiguration(config); - assertEquals(1.0f, strategy.getBrightness(1f), 0.01 /*tolerance*/); + assertEquals(1.0f, strategy.getBrightness(1f), 0.0001f /*tolerance*/); // Check that null returns us to the default configuration. strategy.setBrightnessConfiguration(null); final int N = DISPLAY_LEVELS_NITS.length; final float expectedBrightness = DISPLAY_LEVELS_NITS[N - 1] / DISPLAY_RANGE_NITS[1]; assertEquals(expectedBrightness, - strategy.getBrightness(LUX_LEVELS[N - 1]), 0.01f /*tolerance*/); + strategy.getBrightness(LUX_LEVELS[N - 1]), 0.0001f /*tolerance*/); } @Test public void testPhysicalStrategyRecalculateSplines() { - Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, - BACKLIGHT_RANGE); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); + Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS); + DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc); float[] adjustedNits50p = new float[DISPLAY_RANGE_NITS.length]; for (int i = 0; i < DISPLAY_RANGE_NITS.length; i++) { adjustedNits50p[i] = DISPLAY_RANGE_NITS[i] * 0.5f; } // Default is unadjusted - assertEquals(2.685f, strategy.convertToNits(BACKLIGHT_RANGE[0]), 0.01f /* tolerance */); - assertEquals(478.5f, strategy.convertToNits(BACKLIGHT_RANGE[1]), 0.01f /* tolerance */); + assertEquals(DISPLAY_RANGE_NITS[0], strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[0]), + 0.0001f /* tolerance */); + assertEquals(DISPLAY_RANGE_NITS[1], strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[1]), + 0.0001f /* tolerance */); // When adjustment is turned on, adjustment array is used strategy.recalculateSplines(true, adjustedNits50p); - assertEquals(1.3425f, strategy.convertToNits(BACKLIGHT_RANGE[0]), 0.01f /* tolerance */); - assertEquals(239.25f, strategy.convertToNits(BACKLIGHT_RANGE[1]), 0.01f /* tolerance */); + assertEquals(DISPLAY_RANGE_NITS[0] / 2, + strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[0]), 0.0001f /* tolerance */); + assertEquals(DISPLAY_RANGE_NITS[1] / 2, + strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[1]), 0.0001f /* tolerance */); // When adjustment is turned off, adjustment array is ignored strategy.recalculateSplines(false, adjustedNits50p); - assertEquals(2.685f, strategy.convertToNits(BACKLIGHT_RANGE[0]), 0.01f /* tolerance */); - assertEquals(478.5f, strategy.convertToNits(BACKLIGHT_RANGE[1]), 0.01f /* tolerance */); + assertEquals(DISPLAY_RANGE_NITS[0], strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[0]), + 0.0001f /* tolerance */); + assertEquals(DISPLAY_RANGE_NITS[1], strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[1]), + 0.0001f /* tolerance */); } @Test public void testDefaultStrategyIsPhysical() { Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT, - DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); + DISPLAY_LEVELS_NITS); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc); assertTrue(strategy instanceof BrightnessMappingStrategy.PhysicalMappingStrategy); } @@ -266,15 +283,15 @@ public class BrightnessMappingStrategyTest { int tmp = lux[idx]; lux[idx] = lux[idx+1]; lux[idx+1] = tmp; - Resources res = createResources(lux, DISPLAY_LEVELS_NITS, - DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); + Resources res = createResources(lux, DISPLAY_LEVELS_NITS); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc); assertNull(strategy); // And make sure we get the same result even if it's monotone but not increasing. lux[idx] = lux[idx+1]; - res = createResources(lux, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); - strategy = BrightnessMappingStrategy.create(res); + res = createResources(lux, DISPLAY_LEVELS_NITS); + strategy = BrightnessMappingStrategy.create(res, ddc); assertNull(strategy); } @@ -285,13 +302,13 @@ public class BrightnessMappingStrategyTest { // Make sure it's strictly increasing so that the only failure is the differing array // lengths lux[lux.length - 1] = lux[lux.length - 2] + 1; - Resources res = createResources(lux, DISPLAY_LEVELS_NITS, - DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); + Resources res = createResources(lux, DISPLAY_LEVELS_NITS); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc); assertNull(strategy); res = createResources(lux, DISPLAY_LEVELS_BACKLIGHT); - strategy = BrightnessMappingStrategy.create(res); + strategy = BrightnessMappingStrategy.create(res, ddc); assertNull(strategy); // Extra backlight level @@ -299,43 +316,45 @@ public class BrightnessMappingStrategyTest { DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_BACKLIGHT.length+1); backlight[backlight.length - 1] = backlight[backlight.length - 2] + 1; res = createResources(LUX_LEVELS, backlight); - strategy = BrightnessMappingStrategy.create(res); + strategy = BrightnessMappingStrategy.create(res, ddc); assertNull(strategy); // Extra nits level final float[] nits = Arrays.copyOf(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_NITS.length+1); nits[nits.length - 1] = nits[nits.length - 2] + 1; - res = createResources(LUX_LEVELS, nits, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); - strategy = BrightnessMappingStrategy.create(res); + res = createResources(LUX_LEVELS, nits); + strategy = BrightnessMappingStrategy.create(res, ddc); assertNull(strategy); } @Test public void testPhysicalStrategyRequiresNitsMapping() { Resources res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, - DISPLAY_LEVELS_NITS, EMPTY_FLOAT_ARRAY /*nitsRange*/, BACKLIGHT_RANGE); - BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res); + DISPLAY_LEVELS_NITS); + DisplayDeviceConfig ddc = createDdc(EMPTY_FLOAT_ARRAY /*nitsRange*/); + BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc); assertNull(physical); res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, - DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, EMPTY_INT_ARRAY /*backlightRange*/); - physical = BrightnessMappingStrategy.create(res); + DISPLAY_LEVELS_NITS); + physical = BrightnessMappingStrategy.create(res, ddc); assertNull(physical); res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, - DISPLAY_LEVELS_NITS, EMPTY_FLOAT_ARRAY /*nitsRange*/, - EMPTY_INT_ARRAY /*backlightRange*/); - physical = BrightnessMappingStrategy.create(res); + DISPLAY_LEVELS_NITS); + physical = BrightnessMappingStrategy.create(res, ddc); assertNull(physical); } @Test public void testStrategiesAdaptToUserDataPoint() { - Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, - DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); - assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res)); + Resources res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, + DISPLAY_LEVELS_NITS); + DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE); + assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc)); + ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE); res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); - assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res)); + assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc)); } private static void assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy strategy) { @@ -351,7 +370,7 @@ public class BrightnessMappingStrategyTest { // Then make sure that all control points after the middle lux level are also set to max... for (int i = idx; i < LUX_LEVELS.length; i++) { - assertEquals(strategy.getBrightness(LUX_LEVELS[idx]), 1.0, 0.01 /*tolerance*/); + assertEquals(strategy.getBrightness(LUX_LEVELS[idx]), 1.0, 0.0001f /*tolerance*/); } // ...and that all control points before the middle lux level are strictly less than the @@ -369,12 +388,12 @@ public class BrightnessMappingStrategyTest { strategy.clearUserDataPoints(); for (int i = 0; i < LUX_LEVELS.length; i++) { assertEquals(initialBrightnessLevels[i], strategy.getBrightness(LUX_LEVELS[i]), - 0.01 /*tolerance*/); + 0.0001f /*tolerance*/); } // Now set the middle of the lux range to something just above the minimum. float minBrightness = strategy.getBrightness(LUX_LEVELS[0]); - strategy.addUserDataPoint(LUX_LEVELS[idx], minBrightness + 0.01f); + strategy.addUserDataPoint(LUX_LEVELS[idx], minBrightness + 0.0001f); // Then make sure the curve is still monotonic. prevBrightness = 0f; @@ -389,31 +408,21 @@ public class BrightnessMappingStrategyTest { // be true assuming that there are more than two lux levels in the curve since we picked a // brightness just barely above the minimum for the middle of the curve. minBrightness = (float) MathUtils.pow(minBrightness, MAXIMUM_GAMMA); // Gamma correction. - assertEquals(minBrightness, strategy.getBrightness(LUX_LEVELS[0]), 0.01 /*tolerance*/); - } - - private static float[] toFloatArray(int[] vals) { - float[] newVals = new float[vals.length]; - for (int i = 0; i < vals.length; i++) { - newVals[i] = (float) vals[i]; - } - return newVals; + assertEquals(minBrightness, strategy.getBrightness(LUX_LEVELS[0]), 0.0001f /*tolerance*/); } private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight) { return createResources(luxLevels, brightnessLevelsBacklight, - EMPTY_FLOAT_ARRAY /*brightnessLevelsNits*/, EMPTY_FLOAT_ARRAY /*nitsRange*/, - EMPTY_INT_ARRAY /*backlightRange*/); + EMPTY_FLOAT_ARRAY /*brightnessLevelsNits*/); } - private Resources createResources(int[] luxLevels, float[] brightnessLevelsNits, - float[] nitsRange, int[] backlightRange) { + private Resources createResources(int[] luxLevels, float[] brightnessLevelsNits) { return createResources(luxLevels, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, - brightnessLevelsNits, nitsRange, backlightRange); + brightnessLevelsNits); } private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight, - float[] brightnessLevelsNits, float[] nitsRange, int[] backlightRange) { + float[] brightnessLevelsNits) { Resources mockResources = mock(Resources.class); // For historical reasons, the lux levels resource implicitly defines the first point as 0, // so we need to chop it off of the array the mock resource object returns. @@ -430,15 +439,6 @@ public class BrightnessMappingStrategyTest { com.android.internal.R.array.config_autoBrightnessDisplayValuesNits)) .thenReturn(mockBrightnessLevelNits); - TypedArray mockNitsRange = createFloatTypedArray(nitsRange); - when(mockResources.obtainTypedArray( - com.android.internal.R.array.config_screenBrightnessNits)) - .thenReturn(mockNitsRange); - - when(mockResources.getIntArray( - com.android.internal.R.array.config_screenBrightnessBacklight)) - .thenReturn(backlightRange); - when(mockResources.getInteger( com.android.internal.R.integer.config_screenBrightnessSettingMinimum)) .thenReturn(1); @@ -451,6 +451,21 @@ public class BrightnessMappingStrategyTest { return mockResources; } + private DisplayDeviceConfig createDdc() { + return createDdc(DISPLAY_RANGE_NITS); + } + + private DisplayDeviceConfig createDdc(float[] nitsArray) { + return createDdc(nitsArray, DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT); + } + + private DisplayDeviceConfig createDdc(float[] nitsArray, float[] backlightArray) { + DisplayDeviceConfig mockDdc = mock(DisplayDeviceConfig.class); + when(mockDdc.getNits()).thenReturn(nitsArray); + when(mockDdc.getBrightness()).thenReturn(backlightArray); + return mockDdc; + } + private TypedArray createFloatTypedArray(float[] vals) { TypedArray mockArray = mock(TypedArray.class); when(mockArray.length()).thenAnswer(invocation -> { @@ -488,21 +503,22 @@ public class BrightnessMappingStrategyTest { final float y1 = GAMMA_CORRECTION_SPLINE.interpolate(x1); final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2); final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3); - Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS, - DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources); + + Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc); // Let's start with a validity check: - assertEquals(y1, strategy.getBrightness(x1), 0.01f /* tolerance */); - assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */); - assertEquals(y3, strategy.getBrightness(x3), 0.01f /* tolerance */); + assertEquals(y1, strategy.getBrightness(x1), 0.0001f /* tolerance */); + assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */); + assertEquals(y3, strategy.getBrightness(x3), 0.0001f /* tolerance */); // OK, let's roll: float gamma = 0.5f; strategy.addUserDataPoint(x2, (float) MathUtils.pow(y2, gamma)); - assertEquals(MathUtils.pow(y1, gamma), strategy.getBrightness(x1), 0.01f /* tolerance */); - assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.01f /* tolerance */); - assertEquals(MathUtils.pow(y3, gamma), strategy.getBrightness(x3), 0.01f /* tolerance */); - // The adjustment should be +0.63 (manual calculation). - assertEquals(+0.63f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */); + assertEquals(MathUtils.pow(y1, gamma), strategy.getBrightness(x1), 0.0001f /* tolerance */); + assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.0001f /* tolerance */); + assertEquals(MathUtils.pow(y3, gamma), strategy.getBrightness(x3), 0.0001f /* tolerance */); + // The adjustment should be +0.6308 (manual calculation). + assertEquals(+0.6308f, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */); } @Test @@ -516,39 +532,39 @@ public class BrightnessMappingStrategyTest { final float y1 = GAMMA_CORRECTION_SPLINE.interpolate(x1); final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2); final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3); - Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS, - DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources); + Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc); // Validity check: - assertEquals(y1, strategy.getBrightness(x1), 0.01f /* tolerance */); - assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */); - assertEquals(y3, strategy.getBrightness(x3), 0.01f /* tolerance */); + assertEquals(y1, strategy.getBrightness(x1), 0.0001f /* tolerance */); + assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */); + assertEquals(y3, strategy.getBrightness(x3), 0.0001f /* tolerance */); // Let's roll: float gamma = 0.25f; final float minGamma = 1.0f / MAXIMUM_GAMMA; strategy.addUserDataPoint(x2, (float) MathUtils.pow(y2, gamma)); - assertEquals(MathUtils.pow(y1, minGamma), strategy.getBrightness(x1), - 0.01f /* tolerance */); - assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), - 0.01f /* tolerance */); - assertEquals(MathUtils.pow(y3, minGamma), strategy.getBrightness(x3), - 0.01f /* tolerance */); + assertEquals(MathUtils.pow(y1, minGamma), + strategy.getBrightness(x1), 0.0001f /* tolerance */); + assertEquals(MathUtils.pow(y2, gamma), + strategy.getBrightness(x2), 0.0001f /* tolerance */); + assertEquals(MathUtils.pow(y3, minGamma), + strategy.getBrightness(x3), 0.0001f /* tolerance */); // The adjustment should be +1.0 (maximum adjustment). - assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */); + assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */); } @Test public void testGammaCorrectionExtremeChangeAtCenter() { // Extreme changes (e.g. setting brightness to 0.0 or 1.0) can't be gamma corrected, so we // just make sure the adjustment reflects the change. - Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS, - DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources); - assertEquals(0.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */); + Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc); + assertEquals(0.0f, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */); strategy.addUserDataPoint(2500, 1.0f); - assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */); + assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */); strategy.addUserDataPoint(2500, 0.0f); - assertEquals(-1.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */); + assertEquals(-1.0f, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */); } @Test @@ -562,28 +578,28 @@ public class BrightnessMappingStrategyTest { final float y0 = GAMMA_CORRECTION_SPLINE.interpolate(x0); final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2); final float y4 = GAMMA_CORRECTION_SPLINE.interpolate(x4); - Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS, - DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources); + Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc); // Validity, as per tradition: - assertEquals(y0, strategy.getBrightness(x0), 0.01f /* tolerance */); - assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */); - assertEquals(y4, strategy.getBrightness(x4), 0.01f /* tolerance */); + assertEquals(y0, strategy.getBrightness(x0), 0.0001f /* tolerance */); + assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */); + assertEquals(y4, strategy.getBrightness(x4), 0.0001f /* tolerance */); // Rollin': float adjustment = 0.3f; float gamma = (float) MathUtils.pow(MAXIMUM_GAMMA, -adjustment); strategy.addUserDataPoint(x0, y0 + adjustment); - assertEquals(y0 + adjustment, strategy.getBrightness(x0), 0.01f /* tolerance */); - assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.01f /* tolerance */); - assertEquals(MathUtils.pow(y4, gamma), strategy.getBrightness(x4), 0.01f /* tolerance */); - assertEquals(adjustment, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */); + assertEquals(y0 + adjustment, strategy.getBrightness(x0), 0.0001f /* tolerance */); + assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.0001f /* tolerance */); + assertEquals(MathUtils.pow(y4, gamma), strategy.getBrightness(x4), 0.0001f /* tolerance */); + assertEquals(adjustment, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */); // Similarly, if we set a user data point at (x4, 1.0), the adjustment should be 1 - y4. adjustment = 1.0f - y4; gamma = (float) MathUtils.pow(MAXIMUM_GAMMA, -adjustment); strategy.addUserDataPoint(x4, 1.0f); - assertEquals(MathUtils.pow(y0, gamma), strategy.getBrightness(x0), 0.01f /* tolerance */); - assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.01f /* tolerance */); - assertEquals(1.0f, strategy.getBrightness(x4), 0.01f /* tolerance */); - assertEquals(adjustment, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */); + assertEquals(MathUtils.pow(y0, gamma), strategy.getBrightness(x0), 0.0001f /* tolerance */); + assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.0001f /* tolerance */); + assertEquals(1.0f, strategy.getBrightness(x4), 0.0001f /* tolerance */); + assertEquals(adjustment, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */); } } diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java index 27fce3c37fd9..912da9414165 100644 --- a/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java @@ -45,7 +45,7 @@ public final class PersistentSystemFontConfigTest { public void testWriteRead() throws Exception { long expectedModifiedDate = 1234567890; PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config(); - config.lastModifiedDate = expectedModifiedDate; + config.lastModifiedMillis = expectedModifiedDate; config.updatedFontDirs.add("~~abc"); config.updatedFontDirs.add("~~def"); @@ -65,7 +65,7 @@ public final class PersistentSystemFontConfigTest { PersistentSystemFontConfig.Config another = new PersistentSystemFontConfig.Config(); PersistentSystemFontConfig.loadFromXml(bais, another); - assertThat(another.lastModifiedDate).isEqualTo(expectedModifiedDate); + assertThat(another.lastModifiedMillis).isEqualTo(expectedModifiedDate); assertThat(another.updatedFontDirs).containsExactly("~~abc", "~~def"); assertThat(another.fontFamilies).containsExactly(fontFamily); } @@ -82,7 +82,7 @@ public final class PersistentSystemFontConfigTest { new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) { PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config(); PersistentSystemFontConfig.loadFromXml(bais, config); - assertThat(config.lastModifiedDate).isEqualTo(0); + assertThat(config.lastModifiedMillis).isEqualTo(0); } } diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java index 7771afc8c7f1..843296e31800 100644 --- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java +++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java @@ -151,7 +151,7 @@ public final class UpdatableFontDirTest { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config(); - config.lastModifiedDate = expectedModifiedDate; + config.lastModifiedMillis = expectedModifiedDate; writeConfig(config, mConfigFile); UpdatableFontDir dirForPreparation = new UpdatableFontDir( mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, @@ -507,7 +507,7 @@ public final class UpdatableFontDirTest { File readonlyFile = new File(readonlyDir, "readonly_config.xml"); PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config(); - config.lastModifiedDate = expectedModifiedDate; + config.lastModifiedMillis = expectedModifiedDate; writeConfig(config, readonlyFile); assertThat(readonlyDir.setWritable(false, false)).isTrue(); 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/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java index ddbe81c81d6d..26b34fdd4e04 100644 --- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java @@ -318,7 +318,8 @@ public class PowerStatsServiceTest { assertTrue(pssProto.energyMeasurement.length == ENERGY_METER_COUNT); for (int i = 0; i < pssProto.energyMeasurement.length; i++) { assertTrue(pssProto.energyMeasurement[i].id == i); - assertTrue(pssProto.energyMeasurement[i].timestampMs == i); + assertTrue(pssProto.energyMeasurement[i].timestampMs == + i + mPowerStatsLogger.getStartWallTime()); assertTrue(pssProto.energyMeasurement[i].durationMs == i); assertTrue(pssProto.energyMeasurement[i].energyUws == i); } @@ -359,7 +360,8 @@ public class PowerStatsServiceTest { assertTrue(pssProto.energyConsumerResult.length == ENERGY_CONSUMER_COUNT); for (int i = 0; i < pssProto.energyConsumerResult.length; i++) { assertTrue(pssProto.energyConsumerResult[i].id == i); - assertTrue(pssProto.energyConsumerResult[i].timestampMs == i); + assertTrue(pssProto.energyConsumerResult[i].timestampMs == + i + mPowerStatsLogger.getStartWallTime()); assertTrue(pssProto.energyConsumerResult[i].energyUws == i); assertTrue(pssProto.energyConsumerResult[i].attribution.length == ENERGY_CONSUMER_ATTRIBUTION_COUNT); @@ -420,7 +422,8 @@ public class PowerStatsServiceTest { assertTrue(stateResidency.id == j); assertTrue(stateResidency.totalTimeInStateMs == j); assertTrue(stateResidency.totalStateEntryCount == j); - assertTrue(stateResidency.lastEntryTimestampMs == j); + assertTrue(stateResidency.lastEntryTimestampMs == + j + mPowerStatsLogger.getStartWallTime()); } } } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java index 682a80c9b297..5d2755221288 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java @@ -46,8 +46,8 @@ public class ConfigurationInternalTest { public void test_unrestricted() { ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID) .setUserConfigAllowed(true) - .setAutoDetectionSupported(true) - .setGeoDetectionSupported(true) + .setAutoDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(true) .setAutoDetectionEnabled(true) .setLocationEnabled(true) .setGeoDetectionEnabled(true) @@ -108,8 +108,8 @@ public class ConfigurationInternalTest { public void test_restricted() { ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID) .setUserConfigAllowed(false) - .setAutoDetectionSupported(true) - .setGeoDetectionSupported(true) + .setAutoDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(true) .setAutoDetectionEnabled(true) .setLocationEnabled(true) .setGeoDetectionEnabled(true) @@ -170,8 +170,8 @@ public class ConfigurationInternalTest { public void test_autoDetectNotSupported() { ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID) .setUserConfigAllowed(true) - .setAutoDetectionSupported(false) - .setGeoDetectionSupported(false) + .setAutoDetectionFeatureSupported(false) + .setGeoDetectionFeatureSupported(false) .setAutoDetectionEnabled(true) .setLocationEnabled(true) .setGeoDetectionEnabled(true) @@ -232,8 +232,8 @@ public class ConfigurationInternalTest { public void test_geoDetectNotSupported() { ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID) .setUserConfigAllowed(true) - .setAutoDetectionSupported(true) - .setGeoDetectionSupported(false) + .setAutoDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(false) .setAutoDetectionEnabled(true) .setLocationEnabled(true) .setGeoDetectionEnabled(true) diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java index d2452eaee657..14e0bbd6fa42 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java @@ -364,8 +364,8 @@ public class TimeZoneDetectorServiceTest { // the tests. final boolean geoDetectionEnabled = autoDetectionEnabled; return new ConfigurationInternal.Builder(ARBITRARY_USER_ID) - .setAutoDetectionSupported(true) - .setGeoDetectionSupported(true) + .setAutoDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(true) .setUserConfigAllowed(true) .setAutoDetectionEnabled(autoDetectionEnabled) .setLocationEnabled(geoDetectionEnabled) diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java index c8dba5f40882..f1f8b2f5e81a 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java @@ -90,8 +90,8 @@ public class TimeZoneDetectorStrategyImplTest { private static final ConfigurationInternal CONFIG_INT_USER_RESTRICTED_AUTO_DISABLED = new ConfigurationInternal.Builder(USER_ID) .setUserConfigAllowed(false) - .setAutoDetectionSupported(true) - .setGeoDetectionSupported(true) + .setAutoDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(true) .setAutoDetectionEnabled(false) .setLocationEnabled(true) .setGeoDetectionEnabled(false) @@ -100,8 +100,8 @@ public class TimeZoneDetectorStrategyImplTest { private static final ConfigurationInternal CONFIG_INT_USER_RESTRICTED_AUTO_ENABLED = new ConfigurationInternal.Builder(USER_ID) .setUserConfigAllowed(false) - .setAutoDetectionSupported(true) - .setGeoDetectionSupported(true) + .setAutoDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(true) .setAutoDetectionEnabled(true) .setLocationEnabled(true) .setGeoDetectionEnabled(true) @@ -110,8 +110,8 @@ public class TimeZoneDetectorStrategyImplTest { private static final ConfigurationInternal CONFIG_INT_AUTO_DETECT_NOT_SUPPORTED = new ConfigurationInternal.Builder(USER_ID) .setUserConfigAllowed(true) - .setAutoDetectionSupported(false) - .setGeoDetectionSupported(false) + .setAutoDetectionFeatureSupported(false) + .setGeoDetectionFeatureSupported(false) .setAutoDetectionEnabled(false) .setLocationEnabled(true) .setGeoDetectionEnabled(false) @@ -120,8 +120,8 @@ public class TimeZoneDetectorStrategyImplTest { private static final ConfigurationInternal CONFIG_INT_AUTO_SUPPORTED_GEO_NOT_SUPPORTED = new ConfigurationInternal.Builder(USER_ID) .setUserConfigAllowed(true) - .setAutoDetectionSupported(true) - .setGeoDetectionSupported(false) + .setAutoDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(false) .setAutoDetectionEnabled(true) .setLocationEnabled(true) .setGeoDetectionEnabled(true) @@ -130,8 +130,8 @@ public class TimeZoneDetectorStrategyImplTest { private static final ConfigurationInternal CONFIG_INT_AUTO_DISABLED_GEO_DISABLED = new ConfigurationInternal.Builder(USER_ID) .setUserConfigAllowed(true) - .setAutoDetectionSupported(true) - .setGeoDetectionSupported(true) + .setAutoDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(true) .setAutoDetectionEnabled(false) .setLocationEnabled(true) .setGeoDetectionEnabled(false) @@ -139,8 +139,8 @@ public class TimeZoneDetectorStrategyImplTest { private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_DISABLED = new ConfigurationInternal.Builder(USER_ID) - .setAutoDetectionSupported(true) - .setGeoDetectionSupported(true) + .setAutoDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(true) .setUserConfigAllowed(true) .setAutoDetectionEnabled(true) .setLocationEnabled(true) @@ -149,8 +149,8 @@ public class TimeZoneDetectorStrategyImplTest { private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_ENABLED = new ConfigurationInternal.Builder(USER_ID) - .setAutoDetectionSupported(true) - .setGeoDetectionSupported(true) + .setAutoDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(true) .setUserConfigAllowed(true) .setAutoDetectionEnabled(true) .setLocationEnabled(true) diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java index d319488ba73b..8280cdcb18c4 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java @@ -44,8 +44,8 @@ final class TestSupport { @UserIdInt int userId, boolean geoDetectionEnabled) { return new ConfigurationInternal.Builder(userId) .setUserConfigAllowed(true) - .setAutoDetectionSupported(true) - .setGeoDetectionSupported(true) + .setAutoDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(true) .setAutoDetectionEnabled(true) .setLocationEnabled(true) .setGeoDetectionEnabled(geoDetectionEnabled) 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/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java new file mode 100644 index 000000000000..3025a95be98c --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java @@ -0,0 +1,152 @@ +/* + * 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.policy; + +import static android.view.KeyEvent.ACTION_DOWN; +import static android.view.KeyEvent.ACTION_UP; +import static android.view.KeyEvent.KEYCODE_POWER; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.android.server.policy.SingleKeyGestureDetector.KEY_LONGPRESS; +import static com.android.server.policy.SingleKeyGestureDetector.KEY_VERYLONGPRESS; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.app.Instrumentation; +import android.content.Context; +import android.os.SystemClock; +import android.view.KeyEvent; +import android.view.ViewConfiguration; + +import org.junit.Before; +import org.junit.Test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Test class for {@link SingleKeyGestureDetector}. + * + * Build/Install/Run: + * atest WmTests:SingleKeyGestureTests + */ +public class SingleKeyGestureTests { + private SingleKeyGestureDetector mDetector; + + private int mMaxMultiPressPowerCount = 2; + + private CountDownLatch mShortPressed = new CountDownLatch(1); + private CountDownLatch mLongPressed = new CountDownLatch(1); + private CountDownLatch mVeryLongPressed = new CountDownLatch(1); + private CountDownLatch mMultiPressed = new CountDownLatch(1); + + private final Instrumentation mInstrumentation = getInstrumentation(); + private final Context mContext = mInstrumentation.getTargetContext(); + private long mWaitTimeout; + private long mLongPressTime; + private long mVeryLongPressTime; + + @Before + public void setUp() { + mDetector = new SingleKeyGestureDetector(mContext); + initSingleKeyGestureRules(); + mWaitTimeout = ViewConfiguration.getMultiPressTimeout() + 50; + mLongPressTime = ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout() + 50; + mVeryLongPressTime = mContext.getResources().getInteger( + com.android.internal.R.integer.config_veryLongPressTimeout) + 50; + } + + private void initSingleKeyGestureRules() { + mDetector.addRule(new SingleKeyGestureDetector.SingleKeyRule(KEYCODE_POWER, + KEY_LONGPRESS | KEY_VERYLONGPRESS) { + @Override + int getMaxMultiPressCount() { + return mMaxMultiPressPowerCount; + } + @Override + public void onPress(long downTime) { + mShortPressed.countDown(); + } + + @Override + void onLongPress(long downTime) { + mLongPressed.countDown(); + } + + @Override + void onVeryLongPress(long downTime) { + mVeryLongPressed.countDown(); + } + + @Override + void onMultiPress(long downTime, int count) { + mMultiPressed.countDown(); + assertEquals(mMaxMultiPressPowerCount, count); + } + }); + } + + private void pressKey(long eventTime, int keyCode, long pressTime) { + final KeyEvent keyDown = new KeyEvent(eventTime, eventTime, ACTION_DOWN, + keyCode, 0 /* repeat */, 0 /* metaState */); + mDetector.interceptKey(keyDown); + + // keep press down. + try { + Thread.sleep(pressTime); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + eventTime += pressTime; + final KeyEvent keyUp = new KeyEvent(eventTime, eventTime, ACTION_UP, + keyCode, 0 /* repeat */, 0 /* metaState */); + + mDetector.interceptKey(keyUp); + } + + @Test + public void testShortPress() throws InterruptedException { + final long eventTime = SystemClock.uptimeMillis(); + pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */); + assertTrue(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); + } + + @Test + public void testLongPress() throws InterruptedException { + final long eventTime = SystemClock.uptimeMillis(); + pressKey(eventTime, KEYCODE_POWER, mLongPressTime); + assertTrue(mLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); + } + + @Test + public void testVeryLongPress() throws InterruptedException { + final long eventTime = SystemClock.uptimeMillis(); + pressKey(eventTime, KEYCODE_POWER, mVeryLongPressTime); + assertTrue(mVeryLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); + } + + @Test + public void testMultiPress() throws InterruptedException { + final long eventTime = SystemClock.uptimeMillis(); + pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */); + pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */); + assertTrue(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); + } +} 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 dc702e6bc572..137cf6523caf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -33,6 +33,7 @@ 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; @@ -1443,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); @@ -1454,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 @@ -1845,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/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 8628f89a49c1..dce63ebb0889 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -76,6 +76,7 @@ import android.util.ArraySet; import android.util.Log; import android.util.Slog; +import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IHotwordRecognitionStatusCallback; import com.android.internal.app.IVoiceActionCheckCallback; @@ -643,6 +644,10 @@ public class VoiceInteractionManagerService extends SystemService { } ComponentName findAvailRecognizer(String prefPackage, int userHandle) { + if (prefPackage == null) { + prefPackage = getDefaultRecognizer(); + } + List<ResolveInfo> available = mContext.getPackageManager().queryIntentServicesAsUser( new Intent(RecognitionService.SERVICE_INTERFACE), @@ -670,6 +675,12 @@ public class VoiceInteractionManagerService extends SystemService { } } + @Nullable + public String getDefaultRecognizer() { + String recognizer = mContext.getString(R.string.config_systemSpeechRecognizer); + return TextUtils.isEmpty(recognizer) ? null : recognizer; + } + ComponentName getCurRecognizer(int userHandle) { String curRecognizer = Settings.Secure.getStringForUser( mContext.getContentResolver(), diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 3d43d031868d..4b57fcbf0ad3 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1717,8 +1717,14 @@ public class CarrierConfigManager { * Configs used for APN setup. */ public static final class Apn { - /** Prefix of all Apn.KEY_* constants. */ - private static final String KEY_PREFIX = "apn."; + /** + * Prefix of all Apn.KEY_* constants. + * + * @deprecated Since KEY_PREFIX is unnecessary to public, it will modify to private + * next android generation. + */ + @Deprecated + public static final String KEY_PREFIX = "apn."; /** IPv4 internet protocol */ public static final String PROTOCOL_IPV4 = "IP"; diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java index 5f8e93d02a00..8ad40ed1032c 100644 --- a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java @@ -57,10 +57,10 @@ public class ImsEcbmImplBase { } else if (listener != null && mListener == null) { mListener = listener; } else { - // Fail fast here instead of silently overwriting the listener to another - // listener due to another connection connecting. - throw new IllegalStateException("ImsEcbmImplBase: Listener already set by " - + "another connection."); + // Warn that the listener is being replaced while active + Log.w(TAG, "setListener is being called when there is already an active " + + "listener"); + mListener = listener; } } } diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java index 8e961acc7b36..ec1c7b3a92a8 100644 --- a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java @@ -62,10 +62,10 @@ public class ImsMultiEndpointImplBase { } else if (listener != null && mListener == null) { mListener = listener; } else { - // Fail fast here instead of silently overwriting the listener to another - // listener due to another connection connecting. - throw new IllegalStateException("ImsMultiEndpointImplBase: Listener already" - + " set by another connection."); + // Warn that the listener is being replaced while active + Log.w(TAG, "setListener is being called when there is already an active " + + "listener"); + mListener = listener; } } } diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java index 83b89aa8e814..eb3e8ed5a8e4 100644 --- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java @@ -224,11 +224,10 @@ public class ImsUtImplBase { } else if (listener != null && mUtListener == null) { mUtListener = new ImsUtListener(listener); } else { - // This is a limitation of the current API surface, there can only be one - // listener connected. Fail fast instead of silently overwriting the other - // listener. - throw new IllegalStateException("ImsUtImplBase#setListener: listener already " - + "set by another connected interface!"); + // Warn that the listener is being replaced while active + Log.w(TAG, "setListener is being called when there is already an active " + + "listener"); + mUtListener = new ImsUtListener(listener); } } 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/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt index 7d29cdd2b5c5..a8b7b057fe24 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt @@ -21,8 +21,6 @@ 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.STATUS_BAR_WINDOW_NAME -const val APP_PAIR_SPLIT_DIVIDER = "AppPairSplitDivider" -const val DOCKED_STACK_DIVIDER = "DockedStackDivider" const val WALLPAPER_TITLE = "Wallpaper" fun FlickerTestParameter.statusBarWindowIsAlwaysVisible() { @@ -64,9 +62,9 @@ fun FlickerTestParameter.wallpaperWindowBecomesVisible() { fun FlickerTestParameter.wallpaperWindowBecomesInvisible() { assertWm { - this.showsBelowAppWindow("Wallpaper") + this.showsBelowAppWindow(WALLPAPER_TITLE) .then() - .hidesBelowAppWindow("Wallpaper") + .hidesBelowAppWindow(WALLPAPER_TITLE) } } @@ -103,19 +101,19 @@ fun FlickerTestParameter.noUncoveredRegions( if (allStates) { assertLayers { if (startingBounds == endingBounds) { - this.coversAtLeastRegion(startingBounds) + this.coversAtLeast(startingBounds) } else { - this.coversAtLeastRegion(startingBounds) + this.coversAtLeast(startingBounds) .then() - .coversAtLeastRegion(endingBounds) + .coversAtLeast(endingBounds) } } } else { assertLayersStart { - this.coversAtLeastRegion(startingBounds) + this.coversAtLeast(startingBounds) } assertLayersEnd { - this.coversAtLeastRegion(endingBounds) + this.coversAtLeast(endingBounds) } } } @@ -124,15 +122,15 @@ fun FlickerTestParameter.noUncoveredRegions( fun FlickerTestParameter.navBarLayerIsAlwaysVisible(rotatesScreen: Boolean = false) { if (rotatesScreen) { assertLayers { - this.showsLayer(NAV_BAR_LAYER_NAME) + this.isVisible(NAV_BAR_LAYER_NAME) .then() - .hidesLayer(NAV_BAR_LAYER_NAME) + .isInvisible(NAV_BAR_LAYER_NAME) .then() - .showsLayer(NAV_BAR_LAYER_NAME) + .isVisible(NAV_BAR_LAYER_NAME) } } else { assertLayers { - this.showsLayer(NAV_BAR_LAYER_NAME) + this.isVisible(NAV_BAR_LAYER_NAME) } } } @@ -141,15 +139,15 @@ fun FlickerTestParameter.navBarLayerIsAlwaysVisible(rotatesScreen: Boolean = fal fun FlickerTestParameter.statusBarLayerIsAlwaysVisible(rotatesScreen: Boolean = false) { if (rotatesScreen) { assertLayers { - this.showsLayer(STATUS_BAR_WINDOW_NAME) + this.isVisible(STATUS_BAR_WINDOW_NAME) .then() - hidesLayer(STATUS_BAR_WINDOW_NAME) + .isInvisible(STATUS_BAR_WINDOW_NAME) .then() - .showsLayer(STATUS_BAR_WINDOW_NAME) + .isVisible(STATUS_BAR_WINDOW_NAME) } } else { assertLayers { - this.showsLayer(STATUS_BAR_WINDOW_NAME) + this.isVisible(STATUS_BAR_WINDOW_NAME) } } } @@ -163,10 +161,10 @@ fun FlickerTestParameter.navBarLayerRotatesAndScales( val endingPos = WindowUtils.getNavigationBarPosition(endRotation) assertLayersStart { - this.hasVisibleRegion(NAV_BAR_LAYER_NAME, startingPos) + this.coversExactly(startingPos, NAV_BAR_LAYER_NAME) } assertLayersEnd { - this.hasVisibleRegion(NAV_BAR_LAYER_NAME, endingPos) + this.coversExactly(endingPos, NAV_BAR_LAYER_NAME) } } @@ -179,10 +177,10 @@ fun FlickerTestParameter.statusBarLayerRotatesScales( val endingPos = WindowUtils.getStatusBarPosition(endRotation) assertLayersStart { - this.hasVisibleRegion(STATUS_BAR_WINDOW_NAME, startingPos) + this.coversExactly(startingPos, STATUS_BAR_WINDOW_NAME) } assertLayersEnd { - this.hasVisibleRegion(STATUS_BAR_WINDOW_NAME, endingPos) + this.coversExactly(endingPos, STATUS_BAR_WINDOW_NAME) } } @@ -197,39 +195,41 @@ fun FlickerTestParameter.visibleLayersShownMoreThanOneConsecutiveEntry( fun FlickerTestParameter.appLayerReplacesWallpaperLayer(appName: String) { assertLayers { - this.showsLayer("Wallpaper") + this.isVisible(WALLPAPER_TITLE) .then() - .replaceVisibleLayer("Wallpaper", appName) + .isInvisible(WALLPAPER_TITLE) + .isVisible(appName) } } fun FlickerTestParameter.wallpaperLayerReplacesAppLayer(testApp: IAppHelper) { assertLayers { - this.showsLayer(testApp.getPackage()) + this.isVisible(testApp.getPackage()) .then() - .replaceVisibleLayer(testApp.getPackage(), WALLPAPER_TITLE) + .isInvisible(testApp.getPackage()) + .isVisible(WALLPAPER_TITLE) } } fun FlickerTestParameter.layerAlwaysVisible(packageName: String) { assertLayers { - this.showsLayer(packageName) + this.isVisible(packageName) } } fun FlickerTestParameter.layerBecomesVisible(packageName: String) { assertLayers { - this.hidesLayer(packageName) + this.isInvisible(packageName) .then() - .showsLayer(packageName) + .isVisible(packageName) } } fun FlickerTestParameter.layerBecomesInvisible(packageName: String) { assertLayers { - this.showsLayer(packageName) + this.isVisible(packageName) .then() - .hidesLayer(packageName) + .isInvisible(packageName) } } 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 38a88d372da4..26afb794bb06 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 @@ -153,31 +153,11 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete @Presubmit @Test - fun navBarLayerIsAlwaysVisible() { - Assume.assumeFalse(testSpec.isRotated) - testSpec.navBarLayerIsAlwaysVisible() - } - - @FlakyTest - @Test - fun navBarLayerIsAlwaysVisible_Flaky() { - Assume.assumeTrue(testSpec.isRotated) - testSpec.navBarLayerIsAlwaysVisible() - } + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() @Presubmit @Test - fun statusBarLayerIsAlwaysVisible() { - Assume.assumeFalse(testSpec.isRotated) - testSpec.statusBarLayerIsAlwaysVisible() - } - - @FlakyTest - @Test - fun statusBarLayerIsAlwaysVisible_Flaky() { - Assume.assumeTrue(testSpec.isRotated) - testSpec.statusBarLayerIsAlwaysVisible() - } + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() @Presubmit @Test 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 476708c42c4c..2c4c627a444d 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 @@ -17,9 +17,8 @@ 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.FlickerBuilderProvider @@ -85,55 +84,55 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) { } } - @Postsubmit + @Presubmit @Test fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - @Postsubmit + @Presubmit @Test fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - @Postsubmit + @Presubmit @Test fun visibleWindowsShownMoreThanOneConsecutiveEntry() = testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) - @Postsubmit + @Presubmit @Test fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp) - @Postsubmit + @Presubmit @Test fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - @Postsubmit + @Presubmit @Test fun statusBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - @Postsubmit + @Presubmit @Test fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation) - @FlakyTest + @Presubmit @Test fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation) - @FlakyTest + @Presubmit @Test fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation) - @Postsubmit + @Presubmit @Test fun visibleLayersShownMoreThanOneConsecutiveEntry() = testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() - @Postsubmit + @Presubmit @Test fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible() - @Postsubmit + @Presubmit @Test fun imeAppLayerIsAlwaysVisible() = testSpec.imeAppLayerIsAlwaysVisible(testApp) 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 ac140f505076..2bcdcd9c8219 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 @@ -17,7 +17,7 @@ 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 @@ -91,63 +91,54 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) { } } - @Postsubmit + @Presubmit @Test fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - @Postsubmit + @Presubmit @Test fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - @Postsubmit + @Presubmit @Test fun visibleWindowsShownMoreThanOneConsecutiveEntry() = testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) - @Postsubmit + @Presubmit @Test fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible() - @Postsubmit + @Presubmit @Test fun imeAppWindowBecomesInvisible() = testSpec.imeAppWindowBecomesInvisible(testApp) - @Postsubmit + @Presubmit @Test fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - @Postsubmit + @Presubmit @Test fun statusBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - @Postsubmit + @Presubmit @Test fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0) - @Postsubmit + @Presubmit @Test fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible() - @Postsubmit + @Presubmit @Test fun imeAppLayerBecomesInvisible() = testSpec.imeAppLayerBecomesInvisible(testApp) - @Postsubmit + @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) + fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) - } - @Postsubmit + @Presubmit @Test fun statusBarLayerRotatesScales() { Assume.assumeFalse(testSpec.isRotated) @@ -171,7 +162,8 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) { @JvmStatic fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests(repetitions = 5) + .getConfigNonRotationTests(repetitions = 5, + supportedRotations = listOf(Surface.ROTATION_0)) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt index 212644c04505..3dfa31d64f8c 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 @@ -23,23 +23,23 @@ const val IME_WINDOW_TITLE = "InputMethod" fun FlickerTestParameter.imeLayerBecomesVisible() { assertLayers { - this.hidesLayer(IME_WINDOW_TITLE) + this.isInvisible(IME_WINDOW_TITLE) .then() - .showsLayer(IME_WINDOW_TITLE) + .isVisible(IME_WINDOW_TITLE) } } fun FlickerTestParameter.imeLayerBecomesInvisible() { assertLayers { - this.showsLayer(IME_WINDOW_TITLE) + this.isVisible(IME_WINDOW_TITLE) .then() - .hidesLayer(IME_WINDOW_TITLE) + .isInvisible(IME_WINDOW_TITLE) } } fun FlickerTestParameter.imeAppLayerIsAlwaysVisible(testApp: IAppHelper) { assertLayers { - this.showsLayer(testApp.getPackage()) + this.isVisible(testApp.getPackage()) } } @@ -83,9 +83,8 @@ fun FlickerTestParameter.imeAppWindowBecomesInvisible(testApp: IAppHelper) { fun FlickerTestParameter.imeAppLayerBecomesInvisible(testApp: IAppHelper) { assertLayers { - this.skipUntilFirstAssertion() - .showsLayer(testApp.getPackage()) + this.isVisible(testApp.getPackage()) .then() - .hidesLayer(testApp.getPackage()) + .isInvisible(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 c7a5178a6693..008ba2f72b6c 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 @@ -17,7 +17,7 @@ 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 @@ -89,43 +89,43 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) { } } - @Postsubmit + @Presubmit @Test fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - @Postsubmit + @Presubmit @Test fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - @Postsubmit + @Presubmit @Test fun imeWindowBecomesVisible() = testSpec.imeWindowBecomesVisible() - @Postsubmit + @Presubmit @Test fun appWindowAlwaysVisibleOnTop() = testSpec.appWindowAlwaysVisibleOnTop(testApp.`package`) - @Postsubmit + @Presubmit @Test fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - @Postsubmit + @Presubmit @Test fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() - @Postsubmit + @Presubmit @Test fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation) - @Postsubmit + @Presubmit @Test fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible() - @Postsubmit + @Presubmit @Test fun layerAlwaysVisible() = testSpec.layerAlwaysVisible(testApp.`package`) - @Postsubmit + @Presubmit @Test fun navBarLayerRotatesAndScales() { Assume.assumeFalse(testSpec.isRotated) @@ -139,7 +139,7 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) { testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation) } - @Postsubmit + @Presubmit @Test fun statusBarLayerRotatesScales() { Assume.assumeFalse(testSpec.isRotated) 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 e2a258aea239..18fac6a82de7 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt @@ -17,7 +17,6 @@ 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 @@ -148,17 +147,35 @@ class OpenAppFromOverviewTest(private val testSpec: FlickerTestParameter) { @Test fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`) - @Postsubmit + @Presubmit @Test - fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + fun visibleWindowsShownMoreThanOneConsecutiveEntry() { + Assume.assumeFalse(testSpec.isRotated) testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + } + + @FlakyTest + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + } + + @Presubmit + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + } @FlakyTest @Test - fun visibleLayersShownMoreThanOneConsecutiveEntry() = + fun visibleLayersShownMoreThanOneConsecutiveEntry_Flaky() { + Assume.assumeTrue(testSpec.isRotated) testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + } - @FlakyTest(bugId = 141361128) + @Presubmit @Test fun noUncoveredRegions() = testSpec.noUncoveredRegions(Surface.ROTATION_0, testSpec.config.endRotation) 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 3cc509fe2b8e..20e4b88ff13b 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 @@ -16,13 +16,13 @@ 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 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.SimpleAppHelper @@ -54,7 +54,15 @@ class ChangeAppRotationTest( testSpec: FlickerTestParameter ) : RotationTransition(testSpec) { override val testApp = SimpleAppHelper(instrumentation) - override fun getAppLaunchParams(configuration: Bundle): Map<String, String> = emptyMap() + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { + super.transition(this, it) + setup { + test { + testApp.launchViaIntent(wmHelper) + } + } + } @Presubmit @Test @@ -78,11 +86,11 @@ class ChangeAppRotationTest( @Test fun screenshotLayerBecomesInvisible() { testSpec.assertLayers { - this.showsLayer(testApp.getPackage()) + this.isVisible(testApp.getPackage()) .then() - .showsLayer(SCREENSHOT_LAYER) + .isVisible(SCREENSHOT_LAYER) .then() - .showsLayer(testApp.getPackage()) + .isVisible(testApp.getPackage()) } } @@ -113,7 +121,7 @@ class ChangeAppRotationTest( @Test fun appLayerRotates_StartingPos() { testSpec.assertLayersStart { - this.hasVisibleRegion(testApp.getPackage(), startingPos) + this.coversExactly(startingPos, testApp.getPackage()) } } @@ -121,7 +129,7 @@ class ChangeAppRotationTest( @Test fun appLayerRotates_EndingPos() { testSpec.assertLayersEnd { - this.hasVisibleRegion(testApp.getPackage(), endingPos) + this.coversExactly(endingPos, testApp.getPackage()) } } 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 04ab84dfcd8e..c391112a53ec 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 @@ -17,7 +17,6 @@ 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 @@ -31,36 +30,37 @@ import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation abstract class RotationTransition(protected val testSpec: FlickerTestParameter) { + protected abstract val testApp: StandardAppHelper + 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 abstract val testApp: StandardAppHelper - protected abstract fun getAppLaunchParams(configuration: Bundle): Map<String, String> + protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + } + eachRun { + this.setRotation(testSpec.config.startRotation) + } + } + teardown { + test { + testApp.exit() + } + } + transitions { + this.setRotation(testSpec.config.endRotation) + } + } @FlickerBuilderProvider fun buildFlicker(): FlickerBuilder { return FlickerBuilder(instrumentation).apply { - withTestName { testSpec.name } - repeat { testSpec.config.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - val extras = getAppLaunchParams(testSpec.config) - testApp.launchViaIntent(wmHelper, stringExtras = extras) - } - eachRun { - this.setRotation(testSpec.config.startRotation) - } - } - teardown { - test { - testApp.exit() - } - } - transitions { - this.setRotation(testSpec.config.endRotation) - } + transition(testSpec.config) } } }
\ 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 ef1aeadb31bc..fc5bcc7eef1b 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 @@ -16,7 +16,6 @@ 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 @@ -24,6 +23,7 @@ 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.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.focusDoesNotChange import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper @@ -58,9 +58,18 @@ class SeamlessAppRotationTest( ) : 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() - ) + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { + super.transition(this, it) + setup { + test { + testApp.launchViaIntent(wmHelper, + stringExtras = mapOf( + ActivityOptions.EXTRA_STARVE_UI_THREAD to it.starveUiThread.toString()) + ) + } + } + } @Presubmit @Test @@ -115,7 +124,7 @@ class SeamlessAppRotationTest( @Test fun appLayerRotates() { testSpec.assertLayers { - this.hasVisibleRegion(testApp.`package`, startingPos) + this.coversExactly(startingPos, testApp.`package`) } } @@ -126,12 +135,14 @@ class SeamlessAppRotationTest( companion object { private val testFactory = FlickerTestParameterFactory.getInstance() - private val Bundle.starveUiThread - get() = this.getBoolean(ActivityOptions.EXTRA_STARVE_UI_THREAD, false) + private val Map<String, Any?>.starveUiThread + get() = this.getOrDefault(ActivityOptions.EXTRA_STARVE_UI_THREAD, false) as Boolean - private fun FlickerTestParameter.createConfig(starveUiThread: Boolean): Bundle { - val config = this.config.deepCopy() - config.putBoolean(ActivityOptions.EXTRA_STARVE_UI_THREAD, starveUiThread) + private fun FlickerTestParameter.createConfig( + starveUiThread: Boolean + ): MutableMap<String, Any?> { + val config = this.config.toMutableMap() + config[ActivityOptions.EXTRA_STARVE_UI_THREAD] = starveUiThread return config } 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/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 6de1075d519b..11c10fa44101 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -407,6 +407,8 @@ public class ConnectivityServiceTest { private QosCallbackMockHelper mQosCallbackMockHelper; private QosCallbackTracker mQosCallbackTracker; private VpnManagerService mVpnManagerService; + private TestNetworkCallback mDefaultNetworkCallback; + private TestNetworkCallback mSystemDefaultNetworkCallback; // State variables required to emulate NetworkPolicyManagerService behaviour. private int mUidRules = RULE_NONE; @@ -1547,6 +1549,7 @@ public class ConnectivityServiceTest { @After public void tearDown() throws Exception { + unregisterDefaultNetworkCallbacks(); setAlwaysOnNetworks(false); if (mCellNetworkAgent != null) { mCellNetworkAgent.disconnect(); @@ -9466,6 +9469,10 @@ public class ConnectivityServiceTest { fail("TOO_MANY_REQUESTS never thrown"); } + private UidRange createUidRange(int userId) { + return UidRange.createForUser(UserHandle.of(userId)); + } + private void mockGetApplicationInfo(@NonNull final String packageName, @NonNull final int uid) throws Exception { final ApplicationInfo applicationInfo = new ApplicationInfo(); @@ -9800,6 +9807,54 @@ public class ConnectivityServiceTest { assertEquals(expectedPerAppNetwork, defaultNetwork); assertEquals(expectedOemRequestsSize, defaultRequest.mRequests.size()); } + verifyMultipleDefaultCallbacks(expectedDefaultNetwork, expectedPerAppNetwork); + } + + /** + * Verify default callbacks for 'available' fire as expected. This will only run if + * registerDefaultNetworkCallbacks() was executed prior and will only be different if the + * setOemNetworkPreference() per-app API was used for the current process. + * @param expectedSystemDefault the expected network for the system default. + * @param expectedPerAppDefault the expected network for the current process's default. + */ + private void verifyMultipleDefaultCallbacks( + @NonNull final Network expectedSystemDefault, + @NonNull final Network expectedPerAppDefault) { + if (null != mSystemDefaultNetworkCallback && null != expectedSystemDefault + && mService.mNoServiceNetwork.network() != expectedSystemDefault) { + // getLastAvailableNetwork() is used as this method can be called successively with + // the same network to validate therefore expectAvailableThenValidatedCallbacks + // can't be used. + assertEquals(mSystemDefaultNetworkCallback.getLastAvailableNetwork(), + expectedSystemDefault); + } + if (null != mDefaultNetworkCallback && null != expectedPerAppDefault + && mService.mNoServiceNetwork.network() != expectedPerAppDefault) { + assertEquals(mDefaultNetworkCallback.getLastAvailableNetwork(), + expectedPerAppDefault); + } + } + + private void registerDefaultNetworkCallbacks() { + // Using Manifest.permission.NETWORK_SETTINGS for registerSystemDefaultNetworkCallback() + mServiceContext.setPermission( + Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED); + mSystemDefaultNetworkCallback = new TestNetworkCallback(); + mDefaultNetworkCallback = new TestNetworkCallback(); + mCm.registerSystemDefaultNetworkCallback(mSystemDefaultNetworkCallback, + new Handler(ConnectivityThread.getInstanceLooper())); + mCm.registerDefaultNetworkCallback(mDefaultNetworkCallback); + mServiceContext.setPermission( + Manifest.permission.NETWORK_SETTINGS, PERMISSION_DENIED); + } + + private void unregisterDefaultNetworkCallbacks() { + if (null != mDefaultNetworkCallback) { + mCm.unregisterNetworkCallback(mDefaultNetworkCallback); + } + if (null != mSystemDefaultNetworkCallback) { + mCm.unregisterNetworkCallback(mSystemDefaultNetworkCallback); + } } private void setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest( @@ -9883,6 +9938,7 @@ public class ConnectivityServiceTest { @OemNetworkPreferences.OemNetworkPreference final int networkPref = OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; final int expectedOemPrefRequestSize = 1; + registerDefaultNetworkCallbacks(); // Setup the test process to use networkPref for their default network. setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); @@ -9897,6 +9953,7 @@ public class ConnectivityServiceTest { // Verify that the active network is correct verifyActiveNetwork(TRANSPORT_ETHERNET); + // default NCs will be unregistered in tearDown } @Test @@ -9904,6 +9961,7 @@ public class ConnectivityServiceTest { @OemNetworkPreferences.OemNetworkPreference final int networkPref = OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; final int expectedOemPrefRequestSize = 1; + registerDefaultNetworkCallbacks(); // Setup the test process to use networkPref for their default network. setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); @@ -9924,6 +9982,7 @@ public class ConnectivityServiceTest { mEthernetNetworkAgent.getNetwork()); assertFalse(mCm.isActiveNetworkMetered()); + // default NCs will be unregistered in tearDown } @Test @@ -10080,7 +10139,6 @@ public class ConnectivityServiceTest { /** * Test the tracked default requests clear previous OEM requests on setOemNetworkPreference(). - * @throws Exception */ @Test public void testSetOemNetworkPreferenceClearPreviousOemValues() throws Exception { @@ -10108,9 +10166,8 @@ public class ConnectivityServiceTest { } /** - * Test network priority for preference OEM_NETWORK_PREFERENCE_OEM_PAID following in order: + * Test network priority for preference OEM_NETWORK_PREFERENCE_OEM_PAID in the following order: * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID -> fallback - * @throws Exception */ @Test public void testMultilayerForPreferenceOemPaidEvaluatesCorrectly() @@ -10176,9 +10233,8 @@ public class ConnectivityServiceTest { } /** - * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK following in order: + * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK in the following order: * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID - * @throws Exception */ @Test public void testMultilayerForPreferenceOemPaidNoFallbackEvaluatesCorrectly() @@ -10239,10 +10295,9 @@ public class ConnectivityServiceTest { } /** - * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY following in order: + * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY in the following order: * NET_CAPABILITY_OEM_PAID * This preference should only apply to OEM_PAID networks. - * @throws Exception */ @Test public void testMultilayerForPreferenceOemPaidOnlyEvaluatesCorrectly() @@ -10293,10 +10348,9 @@ public class ConnectivityServiceTest { } /** - * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY following in order: + * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY in the following order: * NET_CAPABILITY_OEM_PRIVATE * This preference should only apply to OEM_PRIVATE networks. - * @throws Exception */ @Test public void testMultilayerForPreferenceOemPrivateOnlyEvaluatesCorrectly() @@ -10346,7 +10400,235 @@ public class ConnectivityServiceTest { true /* shouldDestroyNetwork */); } - private UidRange createUidRange(int userId) { - return UidRange.createForUser(UserHandle.of(userId)); + /** + * Test network priority for preference OEM_NETWORK_PREFERENCE_OEM_PAID in the following order: + * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID -> fallback + */ + @Test + public void testMultipleDefaultNetworksTracksOemNetworkPreferenceOemPaidCorrectly() + throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID; + setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); + final int expectedDefaultRequestSize = 2; + final int expectedOemPrefRequestSize = 3; + registerDefaultNetworkCallbacks(); + + // The fallback as well as the OEM preference should now be tracked. + assertEquals(expectedDefaultRequestSize, mService.mDefaultNetworkRequests.size()); + + // Test lowest to highest priority requests. + // Bring up metered cellular. This will satisfy the fallback network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mCellNetworkAgent.getNetwork()); + + // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // Bring up unmetered Wi-Fi. This will satisfy NET_CAPABILITY_NOT_METERED. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mWiFiNetworkAgent.getNetwork(), + mWiFiNetworkAgent.getNetwork()); + + // Disconnecting unmetered Wi-Fi will put the pref on OEM_PAID and fallback on cellular. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // Disconnecting cellular should keep OEM network on OEM_PAID and fallback will be null. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + null, + mEthernetNetworkAgent.getNetwork()); + + // Disconnecting OEM_PAID will put both on null as it is the last network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + null, + null); + + // default NCs will be unregistered in tearDown + } + + /** + * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK in the following order: + * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID + */ + @Test + public void testMultipleDefaultNetworksTracksOemNetworkPreferenceOemPaidNoFallbackCorrectly() + throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; + setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); + final int expectedDefaultRequestSize = 2; + final int expectedOemPrefRequestSize = 2; + registerDefaultNetworkCallbacks(); + + // The fallback as well as the OEM preference should now be tracked. + assertEquals(expectedDefaultRequestSize, mService.mDefaultNetworkRequests.size()); + + // Test lowest to highest priority requests. + // Bring up metered cellular. This will satisfy the fallback network but not the pref. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mService.mNoServiceNetwork.network()); + + // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // Bring up unmetered Wi-Fi. This will satisfy NET_CAPABILITY_NOT_METERED. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mWiFiNetworkAgent.getNetwork(), + mWiFiNetworkAgent.getNetwork()); + + // Disconnecting unmetered Wi-Fi will put the OEM pref on OEM_PAID and fallback on cellular. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // Disconnecting cellular should keep OEM network on OEM_PAID and fallback will be null. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + null, + mEthernetNetworkAgent.getNetwork()); + + // Disconnecting OEM_PAID puts the fallback on null and the pref on the disconnected net. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + null, + mService.mNoServiceNetwork.network()); + + // default NCs will be unregistered in tearDown + } + + /** + * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY in the following order: + * NET_CAPABILITY_OEM_PAID + * This preference should only apply to OEM_PAID networks. + */ + @Test + public void testMultipleDefaultNetworksTracksOemNetworkPreferenceOemPaidOnlyCorrectly() + throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; + setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); + final int expectedDefaultRequestSize = 2; + final int expectedOemPrefRequestSize = 1; + registerDefaultNetworkCallbacks(); + + // The fallback as well as the OEM preference should now be tracked. + assertEquals(expectedDefaultRequestSize, mService.mDefaultNetworkRequests.size()); + + // Test lowest to highest priority requests. + // Bring up metered cellular. This will satisfy the fallback network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mService.mNoServiceNetwork.network()); + + // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // Bring up unmetered Wi-Fi. The OEM network shouldn't change, the fallback will take Wi-Fi. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mWiFiNetworkAgent.getNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // Disconnecting unmetered Wi-Fi shouldn't change the OEM network with fallback on cellular. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // Disconnecting OEM_PAID will keep the fallback on cellular and nothing for OEM_PAID. + // OEM_PAID_ONLY not supporting a fallback now uses the disconnected network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mService.mNoServiceNetwork.network()); + + // Disconnecting cellular will put the fallback on null and the pref on disconnected. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + null, + mService.mNoServiceNetwork.network()); + + // default NCs will be unregistered in tearDown + } + + /** + * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY in the following order: + * NET_CAPABILITY_OEM_PRIVATE + * This preference should only apply to OEM_PRIVATE networks. + */ + @Test + public void testMultipleDefaultNetworksTracksOemNetworkPreferenceOemPrivateOnlyCorrectly() + throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; + setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); + final int expectedDefaultRequestSize = 2; + final int expectedOemPrefRequestSize = 1; + registerDefaultNetworkCallbacks(); + + // The fallback as well as the OEM preference should now be tracked. + assertEquals(expectedDefaultRequestSize, mService.mDefaultNetworkRequests.size()); + + // Test lowest to highest priority requests. + // Bring up metered cellular. This will satisfy the fallback network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mService.mNoServiceNetwork.network()); + + // Bring up ethernet with OEM_PRIVATE. This will satisfy NET_CAPABILITY_OEM_PRIVATE. + startOemManagedNetwork(false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // Bring up unmetered Wi-Fi. The OEM network shouldn't change, the fallback will take Wi-Fi. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mWiFiNetworkAgent.getNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // Disconnecting unmetered Wi-Fi shouldn't change the OEM network with fallback on cellular. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // Disconnecting OEM_PRIVATE will keep the fallback on cellular. + // OEM_PRIVATE_ONLY not supporting a fallback now uses to the disconnected network. + stopOemManagedNetwork(); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mService.mNoServiceNetwork.network()); + + // Disconnecting cellular will put the fallback on null and pref on disconnected. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + null, + mService.mNoServiceNetwork.network()); + + // default NCs will be unregistered in tearDown } } |